name: toc

Table of Contents


class: inverse, center, middle

Pre-bootcamp HW

Talk about your analysis on the
weather data
in groups of 2-3


Let’s get it all out there

When you think of R what comes to mind?


name: whyr

Why are you here wanting to learn R?


Why should you learn R?

  • R is free, R improves, R has existed for 40+ years
  • Students can show what they have learned to a potential employer easily
  • The support system for R is actually much better than some people say

But as a professor, why should you learn R?

  • R and R Markdown will save you TONS of time. Long term thinking is key.
    • You can easily tweak your code if you need to do another analysis
    • Remembering what drop-down menu some thing is in two years from now in a different program will be hard
    • Remembering to copy-and-paste your updated plots/analysis into your word processor is a pain and error prone

But as a professor, why should you learn R?


class: inverse, center, middle

DEMO in RStudio
with R Markdown


class: center, middle

Analogy

R RStudio DataCamp
Drawing Drawing Drawing

What this bootcamp is

  • Designed for all levels of knowledge and background

What this bootcamp is not

  • A thorough development of machine learning
    techniques applied to big data

  • A discussion on the role of p-values in science

  • A tutorial on how to work with base R subsetting and base R graphics

What I hope you’ll learn

  • That R is not as scary as it used to be.

  • Learning how to Google is an incredibly valuable skill.

  • That having students turn in assignments using R scripts and R Markdown provides an efficient way to check their work and their analyses easily.

What I hope you’ll learn

  • That the R packages you will use in this workshop are the same ones that are used by scientists and graduate programs all over the world

  • That teaching students how to use open-source tools is what is best for them long term


Pieces of advice

  • Scaffold & support as a foreign languages do

  • To be able to use R (and really any other language), students need more than a few assignments and more than a weekly lab

  • Make use of RStudio Projects (students have a really hard time navigating directory structures)

Pieces of advice

My opinion

  • Have students work on writing their code in R script files and documenting their errors
    • This is the same workflow that DataCamp uses

  • Show students R Markdown after a few weeks of working with the script file
    • I’ve found it is hard for students to learn R and R Markdown from the start
    • Better to have them use R Markdown in groups initially

class: inverse, center, middle

R Data Types


The bare minimum needed for understanding today

Vector/variable - Type of vector (int, num, chr, lgl, date)

Data frame - Vectors of (potentially) different types - Each vector has the same number of rows


The bare minimum needed for understanding today

library(tibble)
library(lubridate)
ex1 <- data_frame(
    vec1 = c(1980, 1990, 2000, 2010),
    vec2 = c(1L, 2L, 3L, 4L),
    vec3 = c("low", "low", "high", "high"),
    vec4 = c(TRUE, FALSE, FALSE, FALSE),
    vec5 = ymd(c("2017-05-23", "1776/07/04", "1983-05/31", "1908/04-01"))
  )
ex1


class: center, middle

Welcome to the tidyverse!

The tidyverse is a collection of R packages that share common philosophies and are designed to work together.


Beginning steps

Frequently the first thing to do when given a dataset is to - identify the observational unit, - specify the variables, - give the types of variables you are presented with, and - check that the data is tidy. (TO COME)

This will help with - choosing the appropriate plot, - summarizing the data, and - understanding which inferences/models can be applied.


class: inverse, center, middle name: viz

Data Visualization

Inspired by Hans Rosling


  • What are the variables here?
  • What is the observational unit?
  • How are the variables mapped to aesthetics?

Grammar of Graphics

.left-column[ Wilkinson (2005) laid out the proposed “Grammar of Graphics”]

.right-column[ ]


Grammar of Graphics in R

.left-column[ Wickham implemented the grammar in R in the ggplot2 package]

.right-column[ ]


Grammar of Graphics elsewhere


class: center, middle

# What is a statistical graphic?

## A mapping of
data variables

to
aes()thetic attributes

## of
geom_etric objects.

class: inverse, center, middle

Back to basics


Consider the following data in tidy format:

simple_ex <-
  data_frame(
    A = c(1980, 1990, 2000, 2010),
    B = c(1, 2, 3, 4),
    C = c(3, 2, 1, 2),
    D = c("low", "low", "high", "high")
  )
simple_ex

Consider the following data in tidy format:

A B C D
1980 1 3 low
1990 2 2 low
2000 3 1 high
2010 4 2 high
  • Sketch the graphics below on paper, where the x-axis is variable A and the y-axis is variable B
  1. A scatter plot
  2. A scatter plot where the color of the points corresponds to D
  3. A scatter plot where the size of the points corresponds to C
  4. A line graph
  5. A line graph where the color of the line corresponds to D with points added that are all forestgreen of size 4.

Reproducing the plots in ggplot2

1. A scatterplot

library(ggplot2)
ggplot(data = simple_ex, mapping = aes(x = A, y = B)) + 
  geom_point()


Reproducing the plots in ggplot2

2. A scatter plot where the color of the points corresponds to group

library(ggplot2)
ggplot(data = simple_ex, mapping = aes(x = A, y = B)) + 
  geom_point(mapping = aes(color = D))


Reproducing the plots in ggplot2

3. A scatter plot where the size of the points corresponds to C

library(ggplot2)
ggplot(data = simple_ex, mapping = aes(x = A, y = B, size = C)) + 
  geom_point()


Reproducing the plots in ggplot2

4. A line graph

library(ggplot2)
ggplot(data = simple_ex, mapping = aes(x = A, y = B)) + 
  geom_line()


Reproducing the plots in ggplot2

5. A line graph where the color of the line corresponds to D with points added that are all blue of size 4.

library(ggplot2)
ggplot(data = simple_ex, mapping = aes(x = A, y = B)) + 
  geom_line(mapping = aes(color = D)) +
  geom_point(color = "forestgreen", size = 4)


The Five-Named Graphs

The 5NG of data viz

  • Scatterplot: geom_point()
  • Line graph: geom_line()

  • Histogram: geom_histogram()
  • Boxplot: geom_boxplot()
  • Bar graph: geom_bar()

class: center, middle

More ggplot2 examples


Histogram

library(nycflights13)
ggplot(data = weather, mapping = aes(x = humid)) +
  geom_histogram(bins = 20, color = "black", fill = "darkorange")


Boxplot (broken)

library(nycflights13)
ggplot(data = weather, mapping = aes(x = month, y = humid)) +
  geom_boxplot()


Boxplot (almost fixed)

library(nycflights13)
ggplot(data = weather, mapping = aes(x = month, group = month, y = humid)) +
  geom_boxplot() 

Boxplot (fixed)

library(nycflights13)
ggplot(data = weather, mapping = aes(x = month, group = month, y = humid)) +
  geom_boxplot() +
  scale_x_continuous(breaks = 1:12)


Bar graph

library(fivethirtyeight)
ggplot(data = bechdel, mapping = aes(x = clean_test)) +
  geom_bar()


How about over time?

  • Hop into dplyr
library(dplyr)
year_bins <- c("'70-'74", "'75-'79", "'80-'84", "'85-'89",
               "'90-'94", "'95-'99", "'00-'04", "'05-'09",
               "'10-'13")
bechdel <- bechdel %>%
  mutate(five_year = cut(year, 
                         breaks = seq(1969, 2014, 5), 
                         labels = year_bins)) %>% 
  mutate(clean_test = factor(clean_test, 
                             levels = c("nowomen", "notalk", "men",
                                        "dubious", "ok")))

How about over time? (Stacked)

library(fivethirtyeight)
library(ggplot2)
ggplot(data = bechdel,
       mapping = aes(x = five_year, fill = clean_test)) +
  geom_bar()


How about over time? (Side-by-side)

library(fivethirtyeight)
library(ggplot2)
ggplot(data = bechdel,
       mapping = aes(x = five_year, fill = clean_test)) +
  geom_bar(position = "dodge")


How about over time? (Stacked proportional)

library(fivethirtyeight)
library(ggplot2)
ggplot(data = bechdel,
       mapping = aes(x = five_year, fill = clean_test)) +
  geom_bar(position = "fill", color = "black")


class: center, middle

The tidyverse/ggplot2 is for beginners and
for data science professionals!


Practice

Produce appropriate 5NG with R package & data set in [ ], e.g., [nycflights13 \(\rightarrow\) weather]

  1. Does age predict recline_rude?
    [fivethirtyeight \(\rightarrow\) na.omit(flying)]

  2. Distribution of age by sex
    [okcupiddata \(\rightarrow\) profiles]

  3. Does budget predict rating?
    [ggplot2movies \(\rightarrow\) movies]

  4. Distribution of log base 10 scale of budget_2013
    [fivethirtyeight \(\rightarrow\) bechdel]


HINTS


class: inverse, center, middle

DEMO of ggplot2 in RStudio


class: center, middle

Day 2

Data Wrangling


gapminder data frame in the gapminder package

library(gapminder)
gapminder

Base R versus the tidyverse

Say we wanted mean life expectancy across all years for Asia

# Base R
asia <- gapminder[gapminder$continent == "Asia", ]
mean(asia$lifeExp)
[1] 60.0649

library(dplyr)
gapminder %>% filter(continent == "Asia") %>%
  summarize(mean_exp = mean(lifeExp))

The pipe %>%

   

  • A way to chain together commands

  • It is essentially the dplyr equivalent to the
    + in ggplot2

## The 5NG of data viz

geom_point()
geom_line()
geom_histogram()
geom_boxplot()
geom_bar()


The Five Main Verbs (5MV) of data wrangling

filter()
summarize()
group_by()
mutate()
arrange()


filter()

  • Select a subset of the rows of a data frame.

  • The arguments are the “filters” that you’d like to apply.

library(gapminder); library(dplyr)
gap_2007 <- gapminder %>% filter(year == 2007)
head(gap_2007)
  • Use == to compare a variable to a value

Logical operators

  • Use | to check for any in multiple filters being true:

gapminder %>% 
  filter(year == 2002 | continent == "Europe")


Logical operators

  • Use & or , to check for all of multiple filters being true:

gapminder %>% 
  filter(year == 2002, continent == "Europe")

Logical operators

  • Use %in% to check for any being true
    (shortcut to using | repeatedly with ==)

gapminder %>% 
  filter(country %in% c("Argentina", "Belgium", "Mexico"),
         year %in% c(1987, 1992))


summarize()

  • Any numerical summary that you want to apply to a column of a data frame is specified within summarize().
max_exp_1997 <- gapminder %>% 
  filter(year == 1997) %>% 
  summarize(max_exp = max(lifeExp))
max_exp_1997


Combining summarize() with group_by()

When you’d like to determine a numerical summary for all levels of a different categorical variable

max_exp_1997_by_cont <- gapminder %>% 
  filter(year == 1997) %>% 
  group_by(continent) %>%
  summarize(max_exp = max(lifeExp))
max_exp_1997_by_cont

Without the %>%

It’s hard to appreciate the %>% without seeing what the code would look like without it:

max_exp_1997_by_cont <- 
  summarize(
    group_by(
      filter(
        gapminder, 
          year == 1997), 
      continent),
    max_exp = max(lifeExp))
max_exp_1997_by_cont

ggplot2 revisited

For aggregated data, use geom_col

ggplot(data = max_exp_1997_by_cont, 
       mapping = aes(x = continent, y = max_exp)) +
  geom_col()

The 5MV

  • filter()
  • summarize()
  • group_by()

  • mutate()
- arrange()

mutate()

  • Allows you to
    1. create a new variable with a specific value OR
    2. create a new variable based on other variables OR
    3. change the contents of an existing variable

gap_plus <- gapminder %>% mutate(just_one = 1)
head(gap_plus)

mutate()

  • Allows you to
    1. create a new variable with a specific value OR
    2. create a new variable based on other variables OR
    3. change the contents of an existing variable

gap_w_gdp <- gapminder %>% mutate(gdp = pop * gdpPercap)
head(gap_w_gdp)

mutate()

  • Allows you to
    1. create a new variable with a specific value OR
    2. create a new variable based on other variables OR
    3. change the contents of an existing variable

gap_weird <- gapminder %>% mutate(pop = pop + 1000)
head(gap_weird)

arrange()

  • Reorders the rows in a data frame based on the values of one or more variables

gapminder %>%
  arrange(year, country)

arrange()

  • Can also put into descending order

gapminder %>%
  filter(year > 2000) %>%
  arrange(desc(lifeExp))

Don’t mix up arrange and group_by

  • group_by is used (mostly) with summarize to calculate summaries over groups

  • arrange is used for sorting


Don’t mix up arrange and group_by

This doesn’t really do anything useful

gapminder %>% group_by(year)

Don’t mix up arrange and group_by

But this does

gapminder %>% arrange(year)

Changing of observation unit

True or False > Each of filter, mutate, and arrange change the observational unit.

True or False > group_by() %>% summarize() changes the observational unit.


What is meant by “joining data frames” and
why is it useful?


Does cost of living in a state relate to whether police officers live in the cities they patrol? What about state political ideology?

library(fivethirtyeight); library(readr); library(dplyr)
police2 <- police_locals %>% select(city, all)
ideology <- read_csv("https://ismayc.github.io/Effective-Data-Storytelling-using-the-tidyverse/datasets/ideology.csv")
police_join <- inner_join(x = police2, y = ideology, by = "city")
police_join


url <- paste0("https://ismayc.github.io/",
              "Effective-Data-Storytelling-using-the-tidyverse/",
              "datasets/cost_of_living.csv")
cost_of_living <- read_csv(url)
police_join_cost <- inner_join(
    x = police_join, 
    y = cost_of_living, 
    by = "state"
  )
police_join_cost %>% select(-state) %>% arrange(city)

Does cost of living in a state relate to whether police officers live in the cities they patrol? What about state political ideology?

ggplot(data = police_join_cost,
       mapping = aes(x = index, y = all)) +
  geom_point(mapping = aes(color = state_ideology)) +
  labs(x = "Cost of Living Index", y = "% Officers Living in City")


DEMO of dplyr in RStudio


class: inverse, center, middle

Data Importing and Tidying


tidyverse packages

IMPORT

  • haven for SPSS, SAS, and Stata data files
  • jsonlite for JSON files
  • readxl for XLS and XLSX files
  • readr for CSV and TSV files (and R specific RDS files)


TIDYING

  • tidyr for converting “messy” into “tidy” data frames

name: import

Basics of Importing

We will begin by downloading and importing a variety of different “messy” data sets. You can download all of them in a ZIP file at http://bit.ly/reed-messy-data. The links below go to the original sources. I’ve converted these original sources into different formats.


class: center, middle, inverse

Demonstration in RStudio


Practice


name: tidy class: inverse, middle, center

Tidy Data


  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Each type of observational unit forms a table.

The third point means we don’t mix apples and oranges.


What is Tidy Data?

  1. Each observation forms a row. In other words, each row corresponds to a single instance of an observational unit
  2. Each variable forms a column:
    • Some variables may be used to identify the observational units.
    • For organizational purposes, it’s generally better to put these in the left-hand columns
  3. Each type of observational unit forms a table with the entries in the table corresponding to values.

Differentiating between neat data and tidy data

  • Colloquially, they mean the same thing
  • But in our context, one is a subset of the other.


Neat data is - easy to look at, - organized nicely, and - in table form.

Tidy data is neat but also abides by a set of three rules.


class: center, middle

Drawing


Is this tidy?

year title clean_test budget_2013
1995 Apollo 13 ok 99370665
2005 Brokeback Mountain notalk 16583160
2010 Diary of a Wimpy Kid ok 16023478
1984 Dune dubious 100864980
1984 Ghostbusters notalk 67243320
2003 How to Lose a Guy in 10 Days men 63304348
2011 Iris ok 5696299
2004 Sideways ok 20964279
2000 Songcatcher ok 2435235
2004 Team America: World Police men 24663858
2010 Tron Legacy notalk 213646368
2011 War Horse notalk 72498355

name: demscore

How about this? Is this tidy?

country 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 2002 2007
Albania -9 -9 -9 -9 -9 -9 -9 -9 5 5 7 9
Argentina -9 -1 -1 -9 -9 -9 -8 8 7 7 8 8
Armenia -9 -7 -7 -7 -7 -7 -7 -7 7 -6 5 5
Australia 10 10 10 10 10 10 10 10 10 10 10 10
Austria 10 10 10 10 10 10 10 10 10 10 10 10
Azerbaijan -9 -7 -7 -7 -7 -7 -7 -7 1 -6 -7 -7
Belarus -9 -7 -7 -7 -7 -7 -7 -7 7 -7 -7 -7
Belgium 10 10 10 10 10 10 10 10 10 10 10 8
Bhutan -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -6
Bolivia -4 -3 -3 -4 -7 -7 8 9 9 9 9 8
Brazil 5 5 5 -9 -9 -4 -3 7 8 8 8 8
Bulgaria -7 -7 -7 -7 -7 -7 -7 -7 8 8 9 9

name: whytidy

Why is tidy data important?

  • Think about trying to plot democracy score across years in the simplest way possible.

  • It would be much easier if the data looked like what follows instead so we could put
    • year on the x-axis and
    • dem_score on the y-axis.

Tidy is good

country year dem_score
Argentina 1962 -1
Armenia 1997 -6
Denmark 1962 10
Ethiopia 2007 1
Finland 2007 10
Ireland 1992 10
Libya 1957 -7
Libya 1982 -7
Mexico 1977 -3
Mexico 1982 -3
Spain 1962 -7
Switzerland 1982 10
Ukraine 1997 7

Let’s plot it

  • Plot the line graph for three countries using ggplot
dem_score4 <- dem_score_tidy %>%
  filter(country %in% c("Pakistan", "Portugal", "Uruguay"))
ggplot(data = dem_score4, mapping = aes(x = year, y = dem_score)) +
  geom_line(mapping = aes(color = country))


Tidying

The Life Expectancy by year data

library(readr)
life_exp_df <- read_csv("data/le_mess.csv")
#View(life_exp_df)


Doing the “tidying”/reshaping

library(tidyr)
library(dplyr)
(life_exp_tidy <- life_exp_df %>% 
    gather(key = "year", value = "life_exp", -country))

Tidying

World Health Organization TB data (Stata DTA)

library(haven)
who_df <- read_dta("data/who.dta")
#View(who_df)


WHO ugly…

  • This data set contains strange variable names that will require us to look up their meaning in the corresponding data dictionary.

  • What do we know?
    • country, iso2, and iso3 are redundant and identify the country
    • year is a variable and it looks like it corresponds to each country
  • But what in the world is new_sp_m014? And the other columns?…

First step

Like before, we can gather these non-country and non-year variables together:

who_tidy <- who_df %>% 
  gather(new_sp_m014:newrel_f65, key = "key", value = "value")

We can now see what this has done to the data frame:

View(who_tidy)

Data dictionary saves us some…

  1. The first three letters of entries in the key column correspond to new or old cases of TB.
  2. The next two letters (after the _) correspond to TB type:
    • rel for relapse, ep for extrapulmonary TB
    • sn for smear negative, sp for smear positive
  3. The next letter after the second _ corresponds to the sex of the TB patient.
  4. The remaining numbers correspond to age group:
    • 014 for 0 to 14 years
    • 65 for 65 or older

  • Looking over the entries of key in who_tidy, do you see anything else that doesn’t match the pattern?

  • It may be easier to remember that the entries of key correspond to column names in who_df:

names(who_df)
 [1] "country"      "iso2"         "iso3"         "year"        
 [5] "new_sp_m014"  "new_sp_m1524" "new_sp_m2534" "new_sp_m3544"
 [9] "new_sp_m4554" "new_sp_m5564" "new_sp_m65"   "new_sp_f014" 
[13] "new_sp_f1524" "new_sp_f2534" "new_sp_f3544" "new_sp_f4554"
[17] "new_sp_f5564" "new_sp_f65"   "new_sn_m014"  "new_sn_m1524"
[21] "new_sn_m2534" "new_sn_m3544" "new_sn_m4554" "new_sn_m5564"
[25] "new_sn_m65"   "new_sn_f014"  "new_sn_f1524" "new_sn_f2534"
[29] "new_sn_f3544" "new_sn_f4554" "new_sn_f5564" "new_sn_f65"  
[33] "new_ep_m014"  "new_ep_m1524" "new_ep_m2534" "new_ep_m3544"
[37] "new_ep_m4554" "new_ep_m5564" "new_ep_m65"   "new_ep_f014" 
[41] "new_ep_f1524" "new_ep_f2534" "new_ep_f3544" "new_ep_f4554"
[45] "new_ep_f5564" "new_ep_f65"   "newrel_m014"  "newrel_m1524"
[49] "newrel_m2534" "newrel_m3544" "newrel_m4554" "newrel_m5564"
[53] "newrel_m65"   "newrel_f014"  "newrel_f1524" "newrel_f2534"
[57] "newrel_f3544" "newrel_f4554" "newrel_f5564" "newrel_f65"  

A fix using stringr

You can replace all of the entries in key that contained newrel with new_rel via

library(stringr)
library(dplyr)
who_tidy <- who_tidy %>% 
  mutate(key = str_replace(
      string = key, 
      pattern = "newrel", 
      replacement = "new_rel")
    )

Pulling apart variables

The entry new_sp_m014 is actually four different variables. Use the separate function to pull them apart:

who_tidy <- who_tidy %>% 
  separate(col = key, into = c("new", "type", "sexage"), sep = "_")
who_tidy

One more pull apart

  • We need to pull sexage into two different variables.
  • If you use ?separate, you’ll see that the following is an option:
(who_tidy <- who_tidy %>% 
  separate(col = sexage, into = c("sex", "age"), sep = 1))

Final cleaning

  • iso2 and iso3 are redundant if we have country
  • new is constant since this only has new cases of TB
  • Let’s just ignore missing values here
  • We also know that value is actually cases so we can rename that column.
who_tidy <- who_tidy %>% select(-iso2, -iso3, -new) %>% 
  na.omit() %>% rename("cases" = value)
head(who_tidy, 5)

The power of the %>% (pipe)

Everything we did above can be chained into one sequence:

who_tidy <- who_df %>% 
  gather(new_sp_m014:newrel_f65, key = "key", value = "value") %>%  
  mutate(key = str_replace(key, pattern = "newrel", 
                           replacement = "new_rel")) %>% 
  separate(col = key, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(col = sexage, into = c("sex", "age"), sep = 1) %>% 
  select(-iso2, -iso3, -new) %>% 
  na.omit() %>% 
  rename("cases" = value)

BONUS

A Gapminder tidy dataset read in from a Google Sheet!

  • I’ve updated the gapminder data set available in the gapminder R data package by Jenny Bryan here. Jenny provides instructions for recreating the data.

BONUS

  • You can download the updated data from Google Sheets by running the following in R:
# Link is https://docs.google.com/spreadsheets/d/18L5ZiXd1CQ97XWSqb04x1YQ4ncsEOdR7eHzgX0ZIuPA/edit?usp=sharing
library(googlesheets)
updated_gap <- gs_key("18L5ZiXd1CQ97XWSqb04x1YQ4ncsEOdR7eHzgX0ZIuPA") %>%
  gs_read()
  • A script going through the steps to take the “messy” original Gapminder.org files and turn them into this tidy dataset is available here.

Practice (after the bootcamp)

Try to tidy the other data sets you downloaded and imported or other data sets you have!


name: model class: inverse, center, middle

Data Modeling


Modeling

  1. Experience with ggplot2 package and knowledge of the Grammar of Graphics primes students for modeling
  2. Use of the broom package to unpack regression output into a tidy format

1. ggplot2 Primes Regression

Example:

  • All Alaskan Airlines and Frontier flights leaving NYC in 2013
  • We want to study the relationship between temperature and departure delay
  • For summer (June, July, August) and non-summer months separately

Involves four variables:

  • carrier, temp, dep_delay, summer

1. ggplot2 Primes Regression

flights_subset <- flights %>% 
  filter(carrier == "AS" | carrier == "F9") %>% 
  left_join(weather, by=c("year", "month", "day", "hour", "origin")) %>% 
  filter(dep_delay < 250) %>% 
  mutate(summer = ifelse(month == 6 | month == 7 | month == 8, "Summer Flights", "Non-Summer Flights"))

ggplot(data = flights_subset, 
    mapping = aes(x = temp, y = dep_delay, color = carrier)) + 
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  facet_wrap(~ summer)


1. ggplot2 Primes Regression

Why? Dig deeper into data. Look at origin and dest variables as well:


flights_subset %>% 
  group_by(carrier, origin, dest) %>% 
  summarise(`Number of Flights` = n())

2. broom Package

  • The broom package takes the messy output of built-in modeling functions in R, such as lm, nls, or t.test, and turns them into tidy data frames.
  • Fits in with tidyverse ecosystem
  • This works for many R data types!

2. broom Package

In our case, broom functions take lm objects as inputs and return the following in tidy format!

  • tidy(): regression output table
  • augment(): point-by-point values (fitted values, residuals, predicted values)
  • glance(): scalar summaries like \(R^2\) ,

2. broom Package

Example

library(tidyverse)
library(nycflights13)
library(knitr)
library(broom)
set.seed(2017)

# Load Alaska data, deleting rows that have missing departure delay
# or arrival delay data
alaska_flights <- flights %>% 
  filter(carrier == "AS") %>% 
  filter(!is.na(dep_delay) & !is.na(arr_delay)) %>% 
  sample_n(50)

# Exploratory Data Analysis-----------------------------------------
# Plot of sample of points:
ggplot(data = alaska_flights, mapping = aes(x = dep_delay, y = arr_delay)) + 
   geom_point()


# Correlation coefficient:
alaska_flights %>% summarize(correl = cor(dep_delay, arr_delay))
# Add regression line
ggplot(data = alaska_flights, mapping = aes(x = dep_delay, y = arr_delay)) + 
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "red")


# Fit Regression and Study Output with broom Package-------------------
# Fit regression
options(scipen = 6) # Set scientific notation
delay_fit <- lm(formula = arr_delay ~ dep_delay, data = alaska_flights)

# 1. broom::tidy() regression table with confidence intervals 
# and no p-value stars
regression_table <- delay_fit %>% 
  tidy(conf.int = TRUE)
regression_table

# 2. broom::augment() for point-by-point values
regression_points <- delay_fit %>% 
  augment() %>% 
  select(arr_delay, dep_delay, .fitted, .resid) 
regression_points

# and for prediction
new_flights <- data_frame(dep_delay = c(25, 30, 15))
delay_fit %>% 
  augment(newdata = new_flights)

# 3. broom::glance() scalar summaries of regression
regression_summaries <- delay_fit %>% 
  glance() 
regression_summaries

# Residual Analysis------------------------------------------------------
ggplot(data = regression_points, mapping = aes(x = .resid)) +
  geom_histogram(binwidth=10, color = "white") +
  geom_vline(xintercept = 0, color = "blue")


ggplot(data = regression_points, mapping = aes(x = .fitted, y = .resid)) +
  geom_point() +
  geom_abline(intercept = 0, slope = 0, color = "blue")


ggplot(data = regression_points, mapping = aes(sample = .resid)) +
  stat_qq()


class: center, middle, inverse name: rmarkdown

Data Communicating


What is Markdown?

  • A “plaintext formatting syntax”
  • Type in plain text, render to more complex formats
  • One step beyond writing a txt file
  • Render to HTML, PDF, DOCX, etc. using Pandoc

What does it look like?

.left-column[

  # Header 1
  
  ## Header 2
  
  Normal paragraphs of text go here.
  
  **I'm bold**
  
  [links!](http://rstudio.com)
  
   * Unordered
   * Lists   
   
  And  Tables
  ---- -------
  Like This
  

]

.right-column[ markdown]


What is R Markdown?

  • “Literate programming”
  • Embed R code in a Markdown document
  • Renders textual output along with graphics

.left-column[


```{r chunk1}
library(ggplot2)
library(nycflights13)
pdx_flights <- flights %>% 
  filter(dest == "PDX", month == 5)
nrow(pdx_flights)
```

```{r chunk2}
ggplot(data = pdx_flights,
  mapping = aes(x = arr_delay, 
                y = dep_delay)) +
  geom_point()
```

]

.right-column[

[1] 88

]


Practice

Turn a statistical analysis you have conducted into an R Markdown document.


class: middle

Thanks for attending!

LS0tCnRpdGxlOiBQdXR0aW5nIHRoZSBbYFJgXSgpIGluIFtgUmBdKCllZWQgYW5kIDxicj4gaW4gTGV3aXMgYW5kIENsYVtgUmBdKClrCmF1dGhvcjogIkNoZXN0ZXIgSXNtYXkgPGJyPjxicj4gR2l0SHViOiBbYGlzbWF5Y2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9pc21heWMpIDxicj4gVHdpdHRlcjogIFtgQG9sZF9tYW5fY2hlc3RlcmBdKGh0dHBzOi8vdHdpdHRlci5jb20vb2xkX21hbl9jaGVzdGVyKSIKZGF0ZTogMjAxNy0wNS0yNSAmIDIwMTctMDUtMjYgPGJyPjxicj4gQm9vdGNhbXAgd2Vic2l0ZSBhdCA8aHR0cDovL2JpdC5seS9yYm9vdGNhbXAxNz4gPGJyPiBTbGlkZXMgYXZhaWxhYmxlIGF0IDxodHRwOi8vYml0Lmx5L3Jib290Y2FtcDE3LXNsaWRlcz4Kb3V0cHV0OiAKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgdG9jX2RlcHRoOiAxCiAgICAgIHRoZW1lOiBjb3NtbwogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIGRmX3ByaW50OiBwYWdlZAogICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKbmFtZTogdG9jCgojIFRhYmxlIG9mIENvbnRlbnRzCgotIFtEYXRhIFZpel0oI3ZpeikKLSBbRGF0YSBXcmFuZ2xpbmddKCN3cmFuZ2xlKQotIFtEYXRhIEltcG9ydGluZ10oI2ltcG9ydCkKLSBbRGF0YSBUaWR5aW5nXSgjdGlkeSkKLSBbRGF0YSBNb2RlbGluZ10oI21vZGVsKQotIFtEYXRhIENvbW11bmljYXRpbmddKCNybWFya2Rvd24pCgoKLS0tCgpjbGFzczogaW52ZXJzZSwgY2VudGVyLCBtaWRkbGUKCjwhLS0gV3JpdGUgc2xpZGUgbGluayBvbiB3aGl0ZWJvYXJkIC0tPgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KI29wdGlvbnMoc2VydnIuZGFlbW9uID0gVFJVRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobW9zYWljKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkob2tjdXBpZGRhdGEpCmxpYnJhcnkoa25pdHIpCmZpbHRlciA8LSBkcGx5cjo6ZmlsdGVyCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9OS41LCBmaWcuaGVpZ2h0PTQuNSwgCiAgY29tbWVudD1OQSwgcm93cy5wcmludD0xNikKdGhlbWVfc2V0KHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMjQpKQpgYGAKCiMgUHJlLWJvb3RjYW1wIEhXCgojIyBUYWxrIGFib3V0IHlvdXIgYW5hbHlzaXMgb24gdGhlIDxicj4gYHdlYXRoZXJgIGRhdGEgPGJyPiBpbiBncm91cHMgb2YgMi0zCgotLS0KCiMjIExldCdzIGdldCBpdCBhbGwgb3V0IHRoZXJlCgojIyBXaGVuIHlvdSB0aGluayBvZiBSIHdoYXQgY29tZXMgdG8gbWluZD8KCi0tLQoKbmFtZTogd2h5cgoKIyMgV2h5IGFyZSB5b3UgaGVyZSB3YW50aW5nIHRvIGxlYXJuIFI/CgotIH5+QmVjYXVzZSB5b3UgZW5qb3kgcGFydGFraW5nIGluIFtTY2hhZGVuZnJldWRlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TY2hhZGVuZnJldWRlKX5+Cgo8Y2VudGVyPgo8aW1nIHNyYz0iZmlndXJlL2dvb2dsZV9zY2hvbGFyLnBuZyIgc3R5bGU9IndpZHRoOiA3MDBweDsiLz4KPC9jZW50ZXI+CgotLS0KCiMjIFdoeSBzaG91bGQgeW91IGxlYXJuIFI/CgotIFIgaXMgZnJlZSwgUiBpbXByb3ZlcywgUiBoYXMgZXhpc3RlZCBmb3IgNDArIHllYXJzCi0gU3R1ZGVudHMgY2FuIHNob3cgd2hhdCB0aGV5IGhhdmUgbGVhcm5lZCB0byBhIHBvdGVudGlhbCBlbXBsb3llciBlYXNpbHkKLSBUaGUgc3VwcG9ydCBzeXN0ZW0gZm9yIFIgaXMgYWN0dWFsbHkgbXVjaCBiZXR0ZXIgdGhhbiBzb21lIHBlb3BsZSBzYXkKCgotLS0KCiMjIEJ1dCBhcyBhIHByb2Zlc3Nvciwgd2h5IHNob3VsZCB5b3UgbGVhcm4gUj8KCi0gUiBhbmQgUiBNYXJrZG93biB3aWxsIHNhdmUgeW91IFRPTlMgb2YgdGltZS4gTG9uZyB0ZXJtIHRoaW5raW5nIGlzIGtleS4KICAgIC0gWW91IGNhbiBlYXNpbHkgdHdlYWsgeW91ciBjb2RlIGlmIHlvdSBuZWVkIHRvIGRvIGFub3RoZXIgYW5hbHlzaXMKICAgIC0gUmVtZW1iZXJpbmcgd2hhdCBkcm9wLWRvd24gbWVudSBzb21lIHRoaW5nIGlzIGluIHR3byB5ZWFycyBmcm9tIG5vdyBpbiBhIGRpZmZlcmVudCBwcm9ncmFtIHdpbGwgYmUgaGFyZAogICAgLSBSZW1lbWJlcmluZyB0byBjb3B5LWFuZC1wYXN0ZSB5b3VyIHVwZGF0ZWQgcGxvdHMvYW5hbHlzaXMgaW50byB5b3VyIHdvcmQgcHJvY2Vzc29yIGlzIGEgcGFpbiBhbmQgZXJyb3IgcHJvbmUKCi0tLQoKIyMgQnV0IGFzIGEgcHJvZmVzc29yLCB3aHkgc2hvdWxkIHlvdSBsZWFybiBSPwogICAgCjxjZW50ZXI+CjxpbWcgc3JjPSJmaWd1cmUvdHdlZXQuanBnIiBzdHlsZT0id2lkdGg6IDcwMHB4OyIvPgo8L2NlbnRlcj4KCi0tLQoKY2xhc3M6IGludmVyc2UsIGNlbnRlciwgbWlkZGxlCgojIERFTU8gaW4gUlN0dWRpbyA8YnI+IHdpdGggUiBNYXJrZG93bgoKLS0tCgpjbGFzczogY2VudGVyLCBtaWRkbGUKCiMjIEFuYWxvZ3kKClIgICAgICAgICAgICB8ICBSU3R1ZGlvIHwgIERhdGFDYW1wCjotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06CjxpbWcgc3JjPSJmaWd1cmUvZW5naW5lLmpwZyIgYWx0PSJEcmF3aW5nIiBzdHlsZT0id2lkdGg6IDM1MHB4OyIvPiAgfCAgPGltZyBzcmM9ImZpZ3VyZS9kYXNoYm9hcmQuanBnIiBhbHQ9IkRyYXdpbmciIHN0eWxlPSJ3aWR0aDogMzUwcHg7Ii8+ICB8ICA8aW1nIHNyYz0iZmlndXJlL2luc3RydWN0b3IuanBnIiBhbHQ9IkRyYXdpbmciIHN0eWxlPSJ3aWR0aDogMzUwcHg7Ii8+CgotLS0KCiMjIFdoYXQgdGhpcyBib290Y2FtcCBpcwoKIVtdKGh0dHBzOi8vb3VyY29kaW5nY2x1Yi5naXRodWIuaW8vaW1nL3RpZHl2ZXJzZS5wbmcpCgotIERlc2lnbmVkIGZvciBhbGwgbGV2ZWxzIG9mIGtub3dsZWRnZSBhbmQgYmFja2dyb3VuZAoKLS0tCgojIyBXaGF0IHRoaXMgYm9vdGNhbXAgaXMgbm90CgotIEEgdGhvcm91Z2ggZGV2ZWxvcG1lbnQgb2YgbWFjaGluZSBsZWFybmluZyA8YnI+IHRlY2huaXF1ZXMgYXBwbGllZCB0byBiaWcgZGF0YQoKLS0KCi0gQSBkaXNjdXNzaW9uIG9uIHRoZSByb2xlIG9mIF9wXy12YWx1ZXMgaW4gc2NpZW5jZQoKLS0KCi0gQSB0dXRvcmlhbCBvbiBob3cgdG8gd29yayB3aXRoIGJhc2UgUiBzdWJzZXR0aW5nIGFuZCBiYXNlIFIgZ3JhcGhpY3MgCgotLS0KCiMjIFdoYXQgSSBob3BlIHlvdSdsbCBsZWFybgoKLSBUaGF0IFIgaXMgbm90IGFzIHNjYXJ5IGFzIGl0IHVzZWQgdG8gYmUuICAKCi0tCgotIExlYXJuaW5nIGhvdyB0byBHb29nbGUgaXMgYW4gaW5jcmVkaWJseSB2YWx1YWJsZSBza2lsbC4KCi0tCgotIFRoYXQgaGF2aW5nIHN0dWRlbnRzIHR1cm4gaW4gYXNzaWdubWVudHMgdXNpbmcgUiBzY3JpcHRzIGFuZCBSIE1hcmtkb3duIHByb3ZpZGVzIGFuIGVmZmljaWVudCB3YXkgdG8gY2hlY2sgdGhlaXIgd29yayBhbmQgdGhlaXIgYW5hbHlzZXMgZWFzaWx5LgoKLS0tCgojIyBXaGF0IEkgaG9wZSB5b3UnbGwgbGVhcm4KCi0gVGhhdCB0aGUgUiBwYWNrYWdlcyB5b3Ugd2lsbCB1c2UgaW4gdGhpcyB3b3Jrc2hvcCBhcmUgdGhlIHNhbWUgb25lcyB0aGF0IGFyZSB1c2VkIGJ5IHNjaWVudGlzdHMgYW5kIGdyYWR1YXRlIHByb2dyYW1zIGFsbCBvdmVyIHRoZSB3b3JsZAoKLS0KCi0gVGhhdCB0ZWFjaGluZyBzdHVkZW50cyBob3cgdG8gdXNlIG9wZW4tc291cmNlIHRvb2xzIGlzIHdoYXQgaXMgYmVzdCBmb3IgdGhlbSBsb25nIHRlcm0KCi0tCgotIFRoYXQgc3R1ZGVudHMgd2l0aCBubyBwcm9ncmFtbWluZyBiYWNrZ3JvdW5kIGNhbiBkbyBncmVhdCB0aGluZ3MgaW4gb25seSBhIGZldyB3ZWVrcwogICAgLSBbU29jaW9sb2d5L0NyaW1pbmFsIEp1c3RpY2UgbWFqb3JzIGF0IFBhY2lmaWMgVS5dKGh0dHBzOi8vaXNtYXljLmdpdGh1Yi5pby9zb2MzMDFfczIwMTcvZ3JvdXAtcHJvamVjdHMvaW5kZXguaHRtbCkKICAgIC0gW0Egd2lkZSByYW5nZSBvZiBzdHVkZW50cyBhdCBNaWRkbGVidXJ5IENvbGxlZ2VdKGh0dHBzOi8vcnVkZWJveWJlcnQuZ2l0aHViLmlvL01BVEgxMTYvUFMvZmluYWxfcHJvamVjdC9maW5hbF9wcm9qZWN0X291dGxpbmUuaHRtbCkKCi0tLQoKIyMgUGllY2VzIG9mIGFkdmljZQoKPGNlbnRlcj4KPGEgaHJlZj0iaHR0cDovL2dpcGh5LmNvbS9naWZzL3Bvb2wtZGl2aW5nLXN5bmNocm9uaXplZC1zd2ltbWluZy1wRFd0d0s3RDJJbEZ1Ij48aW1nIHNyYz0iZmlndXJlL2dpcGh5Mi5naWYiIHN0eWxlPSJ3aWR0aDogMzAwcHg7Ii8+PC9hPgo8L2NlbnRlcj4KCi0gU2NhZmZvbGQgJiBzdXBwb3J0IGFzIGEgZm9yZWlnbiBsYW5ndWFnZXMgZG8KCi0tCgotIFRvIGJlIGFibGUgdG8gdXNlIFIgKGFuZCByZWFsbHkgYW55IG90aGVyIGxhbmd1YWdlKSwgc3R1ZGVudHMgbmVlZCBtb3JlIHRoYW4gYSBmZXcgYXNzaWdubWVudHMgYW5kIG1vcmUgdGhhbiBhIHdlZWtseSBsYWIKCi0tCgotIE1ha2UgdXNlIG9mIFJTdHVkaW8gUHJvamVjdHMgKHN0dWRlbnRzIGhhdmUgYSByZWFsbHkgaGFyZCB0aW1lIG5hdmlnYXRpbmcgZGlyZWN0b3J5IHN0cnVjdHVyZXMpCgoKLS0tCgojIyBQaWVjZXMgb2YgYWR2aWNlCgojIyMgTXkgb3BpbmlvbgoKLSBIYXZlIHN0dWRlbnRzIHdvcmsgb24gd3JpdGluZyB0aGVpciBjb2RlIGluIFIgc2NyaXB0IGZpbGVzIGFuZCBkb2N1bWVudGluZyB0aGVpciBlcnJvcnMKICAgIC0gVGhpcyBpcyB0aGUgc2FtZSB3b3JrZmxvdyB0aGF0IERhdGFDYW1wIHVzZXMKCi0tCgotIFNob3cgc3R1ZGVudHMgW1IgTWFya2Rvd24gYWZ0ZXIgYSBmZXcgd2Vla3NdKGh0dHBzOi8vaXNtYXljLmdpdGh1Yi5pby9zb2MzMDFfczIwMTcvc2NoZWR1bGUvKSBvZiB3b3JraW5nIHdpdGggdGhlIHNjcmlwdCBmaWxlCiAgICAtIEkndmUgZm91bmQgaXQgaXMgaGFyZCBmb3Igc3R1ZGVudHMgdG8gbGVhcm4gUiBhbmQgUiBNYXJrZG93biBmcm9tIHRoZSBzdGFydAogICAgLSBCZXR0ZXIgdG8gaGF2ZSB0aGVtIHVzZSBSIE1hcmtkb3duIGluIGdyb3VwcyBpbml0aWFsbHkKCgotLS0KCgpjbGFzczogaW52ZXJzZSwgY2VudGVyLCBtaWRkbGUKCiMgUiBEYXRhIFR5cGVzCgotLS0KCiMjIFRoZSBiYXJlIG1pbmltdW0gbmVlZGVkIGZvciB1bmRlcnN0YW5kaW5nIHRvZGF5CgpWZWN0b3IvdmFyaWFibGUKICAtIFR5cGUgb2YgdmVjdG9yIChgaW50YCwgYG51bWAsIGBjaHJgLCBgbGdsYCwgYGRhdGVgKQoKLS0KCkRhdGEgZnJhbWUKICAtIFZlY3RvcnMgb2YgKHBvdGVudGlhbGx5KSBkaWZmZXJlbnQgdHlwZXMKICAtIEVhY2ggdmVjdG9yIGhhcyB0aGUgc2FtZSBudW1iZXIgb2Ygcm93cwoKLS0tCgojIyBUaGUgYmFyZSBtaW5pbXVtIG5lZWRlZCBmb3IgdW5kZXJzdGFuZGluZyB0b2RheQoKYGBge3IgZXZhbD1GQUxTRX0KbGlicmFyeSh0aWJibGUpCmxpYnJhcnkobHVicmlkYXRlKQpleDEgPC0gZGF0YV9mcmFtZSgKICAgIHZlYzEgPSBjKDE5ODAsIDE5OTAsIDIwMDAsIDIwMTApLAogICAgdmVjMiA9IGMoMUwsIDJMLCAzTCwgNEwpLAogICAgdmVjMyA9IGMoImxvdyIsICJsb3ciLCAiaGlnaCIsICJoaWdoIiksCiAgICB2ZWM0ID0gYyhUUlVFLCBGQUxTRSwgRkFMU0UsIEZBTFNFKSwKICAgIHZlYzUgPSB5bWQoYygiMjAxNy0wNS0yMyIsICIxNzc2LzA3LzA0IiwgIjE5ODMtMDUvMzEiLCAiMTkwOC8wNC0wMSIpKQogICkKZXgxCmBgYAoKLS0KCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKZXgxIDwtIGRhdGFfZnJhbWUoCiAgICB2ZWMxID0gYygxOTgwLCAxOTkwLCAyMDAwLCAyMDEwKSwKICAgIHZlYzIgPSBjKDFMLCAyTCwgM0wsIDRMKSwKICAgIHZlYzMgPSBjKCJsb3ciLCAibG93IiwgImhpZ2giLCAiaGlnaCIpLAogICAgdmVjNCA9IGMoVFJVRSwgRkFMU0UsIEZBTFNFLCBGQUxTRSksCiAgICB2ZWM1ID0geW1kKGMoIjIwMTctMDUtMjMiLCAiMTc3Ni83LzA0IiwgIjE5ODMtNS8zMSIsICIxOTA4LzA0LTEiKSkKICApCmV4MQpgYGAKICAKLS0tCgpjbGFzczogY2VudGVyLCBtaWRkbGUgIAogIAojIyBXZWxjb21lIHRvIHRoZSBbdGlkeXZlcnNlXShodHRwczovL2Jsb2cucnN0dWRpby5vcmcvMjAxNi8wOS8xNS90aWR5dmVyc2UtMS0wLTAvKSEKICAKVGhlIGB0aWR5dmVyc2VgIGlzIGEgY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzIHRoYXQgc2hhcmUgY29tbW9uIHBoaWxvc29waGllcyBhbmQgYXJlIGRlc2lnbmVkIHRvIHdvcmsgdG9nZXRoZXIuIDxicj48YnI+IAogIAo8YSBocmVmPSJodHRwOi8vdGlkeXZlcnNlLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciPjxpbWcgc3JjPSJmaWd1cmUvdGlkeXZlcnNlLnBuZyIgc3R5bGU9IndpZHRoOiAyMDBweDsiLz48YT4KCi0tLQoKCiMjIEJlZ2lubmluZyBzdGVwcwoKRnJlcXVlbnRseSB0aGUgZmlyc3QgdGhpbmcgdG8gZG8gd2hlbiBnaXZlbiBhIGRhdGFzZXQgaXMgdG8KLSBpZGVudGlmeSB0aGUgb2JzZXJ2YXRpb25hbCB1bml0LAotIHNwZWNpZnkgdGhlIHZhcmlhYmxlcywKLSBnaXZlIHRoZSB0eXBlcyBvZiB2YXJpYWJsZXMgeW91IGFyZSBwcmVzZW50ZWQgd2l0aCwgYW5kCi0gY2hlY2sgdGhhdCB0aGUgZGF0YSBpcyA8dT50aWR5PC91Pi4gKFRPIENPTUUpCgpUaGlzIHdpbGwgaGVscCB3aXRoIAotIGNob29zaW5nIHRoZSBhcHByb3ByaWF0ZSBwbG90LCAKLSBzdW1tYXJpemluZyB0aGUgZGF0YSwgYW5kIAotIHVuZGVyc3RhbmRpbmcgd2hpY2ggaW5mZXJlbmNlcy9tb2RlbHMgY2FuIGJlIGFwcGxpZWQuCgotLS0KCmNsYXNzOiBpbnZlcnNlLCBjZW50ZXIsIG1pZGRsZQpuYW1lOiB2aXoKCgojIERhdGEgVmlzdWFsaXphdGlvbgoKPGEgaHJlZj0iaHR0cDovL2dpdHNlbnNlLmdpdGh1Yi5pby9pbWFnZXMvd2VhbHRoLmdpZiI+PGltZyBzcmM9ImZpZ3VyZS93ZWFsdGguZ2lmIiBzdHlsZT0id2lkdGg6IDc3MHB4OyIvPjwvYT4KCkluc3BpcmVkIGJ5IFtIYW5zIFJvc2xpbmddKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9amJrU1JMWVNvam8pCgotLS0KCmBgYHtyIGVjaG89RkFMU0UsZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAsIGZpZy5hbGlnbj0nY2VudGVyJ30KbGlicmFyeShnYXBtaW5kZXIpCm9wdGlvbnMoc2NpcGVuID0gOTkpCiNnYXBfd2l0aF9jb2xvcnMgPC0KIyAgZGF0YS5mcmFtZShnYXBtaW5kZXIsCiMgICAgICAgICAgICAgY2MgPSBJKGNvdW50cnlfY29sb3JzW21hdGNoKGdhcG1pbmRlciRjb3VudHJ5LAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcyhjb3VudHJ5X2NvbG9ycykpXSkpCgpnYXBtaW5kZXIgJT4lIGZpbHRlcih5ZWFyID09IDE5OTIpICU+JQogIGdncGxvdChhZXMoeCA9IGxvZyhnZHBQZXJjYXAsIGJhc2UgPSAxMCksIHkgPSBsaWZlRXhwLCBjb2xvciA9IGNvbnRpbmVudCwKICAgICAgICAgICAgIHNpemUgPSBwb3ApKSArCiAgZ2VvbV9wb2ludCgpICsgeGxhYignR3Jvc3MgRG9tZXN0aWMgUHJvZHVjdCAobG9nIHNjYWxlKScpICsgeWxhYignTGlmZSBFeHBlY3RhbmN5IGF0IGJpcnRoICh5ZWFycyknKSArIGdndGl0bGUoIkdhcG1pbmRlciBmb3IgMTk5MiIpCgojKwojICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ2FwbWluZGVyOjpjb250aW5lbnRfY29sb3JzKQpgYGAKCgotIFdoYXQgYXJlIHRoZSB2YXJpYWJsZXMgaGVyZT8KLSBXaGF0IGlzIHRoZSBvYnNlcnZhdGlvbmFsIHVuaXQ/Ci0gSG93IGFyZSB0aGUgdmFyaWFibGVzIG1hcHBlZCB0byBhZXN0aGV0aWNzPwoKLS0tCgoKIyMgR3JhbW1hciBvZiBHcmFwaGljcwoKLmxlZnQtY29sdW1uWwpXaWxraW5zb24gKDIwMDUpIGxhaWQgb3V0IHRoZSBwcm9wb3NlZCAiR3JhbW1hciBvZiBHcmFwaGljcyIKXQoKLnJpZ2h0LWNvbHVtblsKPGEgaHJlZj0iaHR0cDovL3d3dy5wb3dlbGxzLmNvbS9ib29rL3RoZS1ncmFtbWFyLW9mLWdyYXBoaWNzLTk3ODAzODcyNDU0NDciPjxpbWcgc3JjPSJmaWd1cmUvZ3JhcGhpY3MuanBnIiBzdHlsZT0id2lkdGg6IDMwMHB4OyI+PC9hPgpdCgotLS0KCiMjIEdyYW1tYXIgb2YgR3JhcGhpY3MgaW4gUgoKLmxlZnQtY29sdW1uWwpXaWNraGFtIGltcGxlbWVudGVkIHRoZSBncmFtbWFyIGluIFIgaW4gdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlCl0KCi5yaWdodC1jb2x1bW5bCjxhIGhyZWY9Imh0dHA6Ly93d3cucG93ZWxscy5jb20vYm9vay9nZ3Bsb3QyLWVsZWdhbnQtZ3JhcGhpY3MtZm9yLWRhdGEtYW5hbHlzaXMtOTc4MzMxOTI0Mjc1MC82OC00MjgiPjxpbWcgc3JjPSJmaWd1cmUvZ2dwbG90Mi5qcGciIHN0eWxlPSJ3aWR0aDogMzAwcHg7Ij48L2E+Cl0KCi0tLQoKIyMgR3JhbW1hciBvZiBHcmFwaGljcyBlbHNld2hlcmUKCi0gW01ha2UgcGxvdHMgb25saW5lIHdpdGggcGxvdGx5XShodHRwczovL3Bsb3QubHkvKQoKLSBbTGVsYW5kIFdpbGtpbnNvbl0oaHR0cHM6Ly9yZXNlYXJjaC50YWJsZWF1LmNvbS91c2VyL2xlbGFuZC13aWxraW5zb24pIHdvcmtzIGF0IFtUYWJsZWF1XShodHRwczovL3d3dy50YWJsZWF1LmNvbS8pCgotIFRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIHByb3ZpZGVzIGEgdGhlb3JldGljYWwgZnJhbWV3b3JrIGZvciBidWlsZGluZyBhbmQgZGVjaXBoZXJpbmcgc3RhdGlzdGljYWwgZ3JhcGhpY3MKCi0tLQoKY2xhc3M6IGNlbnRlciwgbWlkZGxlCgojIFdoYXQgaXMgYSBzdGF0aXN0aWNhbCBncmFwaGljPwotLQoKIyMgQSBgbWFwcGluZ2Agb2YgPGJyPiBgZGF0YWAgdmFyaWFibGVzCi0tCgojIyB0byA8YnI+IGBhZXMoKWB0aGV0aWMgYXR0cmlidXRlcwoKLS0KIyMgb2YgPGJyPiBgZ2VvbV9gZXRyaWMgb2JqZWN0cy4KCi0tLQoKY2xhc3M6IGludmVyc2UsIGNlbnRlciwgbWlkZGxlCgojIyBCYWNrIHRvIGJhc2ljcwoKLS0tCgojIyMgQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBkYXRhIGluIHRpZHkgZm9ybWF0OgoKYGBge3J9CnNpbXBsZV9leCA8LQogIGRhdGFfZnJhbWUoCiAgICBBID0gYygxOTgwLCAxOTkwLCAyMDAwLCAyMDEwKSwKICAgIEIgPSBjKDEsIDIsIDMsIDQpLAogICAgQyA9IGMoMywgMiwgMSwgMiksCiAgICBEID0gYygibG93IiwgImxvdyIsICJoaWdoIiwgImhpZ2giKQogICkKc2ltcGxlX2V4CmBgYAoKPCEtLSBDb3B5IHRvIGNoYWxrYm9hcmQvd2hpdGVib2FyZCAtLT4KCi0tLQoKIyMjIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZGF0YSBpbiB0aWR5IGZvcm1hdDoKCmBgYHtyIGVjaG89RkFMU0V9CnNpbXBsZV9leCAlPiUga25pdHI6OmthYmxlKGZvcm1hdD0iaHRtbCIpCmBgYAoKCi0gU2tldGNoIHRoZSBncmFwaGljcyBiZWxvdyBvbiBwYXBlciwgd2hlcmUgdGhlIGB4YC1heGlzIGlzIHZhcmlhYmxlIGBBYCBhbmQgdGhlIGB5YC1heGlzIGlzIHZhcmlhYmxlIGBCYAoKMS4gPHNtYWxsPkEgc2NhdHRlciBwbG90PC9zbWFsbD4KMS4gPHNtYWxsPkEgc2NhdHRlciBwbG90IHdoZXJlIHRoZSBgY29sb3JgIG9mIHRoZSBwb2ludHMgY29ycmVzcG9uZHMgdG8gYERgPC9zbWFsbD4KMS4gPHNtYWxsPkEgc2NhdHRlciBwbG90IHdoZXJlIHRoZSBgc2l6ZWAgb2YgdGhlIHBvaW50cyBjb3JyZXNwb25kcyB0byBgQ2A8L3NtYWxsPgoxLiA8c21hbGw+QSBsaW5lIGdyYXBoPC9zbWFsbD4KMS4gPHNtYWxsPkEgbGluZSBncmFwaCB3aGVyZSB0aGUgYGNvbG9yYCBvZiB0aGUgbGluZSBjb3JyZXNwb25kcyB0byBgRGAgd2l0aCBwb2ludHMgYWRkZWQgdGhhdCBhcmUgYWxsIGZvcmVzdGdyZWVuIG9mIHNpemUgNC48L3NtYWxsPgoKLS0tCgojIyBSZXByb2R1Y2luZyB0aGUgcGxvdHMgaW4gPHNtYWxsPmBnZ3Bsb3QyYDwvc21hbGw+CgojIyMgMS4gQSBzY2F0dGVycGxvdAoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIG1hcHBpbmcgPSBhZXMoeCA9IEEsIHkgPSBCKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCi0tCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD00LjV9CmdncGxvdChkYXRhID0gc2ltcGxlX2V4LCBhZXMoeCA9IEEsIHkgPSBCKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgoKLS0tCgoKIyMgUmVwcm9kdWNpbmcgdGhlIHBsb3RzIGluIDxzbWFsbD5gZ2dwbG90MmA8L3NtYWxsPgoKIyMjIDIuIEEgc2NhdHRlciBwbG90IHdoZXJlIHRoZSBgY29sb3JgIG9mIHRoZSBwb2ludHMgY29ycmVzcG9uZHMgdG8gYGdyb3VwYAoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIG1hcHBpbmcgPSBhZXMoeCA9IEEsIHkgPSBCKSkgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBEKSkKYGBgCi0tCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD00LjV9CmdncGxvdChkYXRhID0gc2ltcGxlX2V4LCBtYXBwaW5nID0gYWVzKHggPSBBLCB5ID0gQikpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gRCkpCmBgYAoKCi0tLQoKIyMgUmVwcm9kdWNpbmcgdGhlIHBsb3RzIGluIDxzbWFsbD5gZ2dwbG90MmA8L3NtYWxsPgoKIyMjIDMuIEEgc2NhdHRlciBwbG90IHdoZXJlIHRoZSBgc2l6ZWAgb2YgdGhlIHBvaW50cyBjb3JyZXNwb25kcyB0byBgQ2AKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRhID0gc2ltcGxlX2V4LCBtYXBwaW5nID0gYWVzKHggPSBBLCB5ID0gQiwgc2l6ZSA9IEMpKSArIAogIGdlb21fcG9pbnQoKQpgYGAKLS0KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTQuNX0KZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIG1hcHBpbmcgPSBhZXMoeCA9IEEsIHkgPSBCLCBzaXplID0gQykpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKCi0tLQoKIyMgUmVwcm9kdWNpbmcgdGhlIHBsb3RzIGluIDxzbWFsbD5gZ2dwbG90MmA8L3NtYWxsPgoKIyMjIDQuIEEgbGluZSBncmFwaAoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIG1hcHBpbmcgPSBhZXMoeCA9IEEsIHkgPSBCKSkgKyAKICBnZW9tX2xpbmUoKQpgYGAKLS0KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTQuNX0KZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIGFlcyh4ID0gQSwgeSA9IEIpKSArIAogIGdlb21fbGluZSgpCmBgYAoKCi0tLQoKIyMgUmVwcm9kdWNpbmcgdGhlIHBsb3RzIGluIDxzbWFsbD5gZ2dwbG90MmA8L3NtYWxsPgoKIyMjIDUuIEEgbGluZSBncmFwaCB3aGVyZSB0aGUgYGNvbG9yYCBvZiB0aGUgbGluZSBjb3JyZXNwb25kcyB0byBgRGAgd2l0aCBwb2ludHMgYWRkZWQgdGhhdCBhcmUgYWxsIGJsdWUgb2Ygc2l6ZSA0LgoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBzaW1wbGVfZXgsIG1hcHBpbmcgPSBhZXMoeCA9IEEsIHkgPSBCKSkgKyAKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IEQpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJmb3Jlc3RncmVlbiIsIHNpemUgPSA0KQpgYGAKLS0KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTR9CmdncGxvdChkYXRhID0gc2ltcGxlX2V4LCBtYXBwaW5nID0gYWVzKHggPSBBLCB5ID0gQikpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoY29sb3IgPSBEKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZm9yZXN0Z3JlZW4iLCBzaXplID0gNCkKYGBgCgotLS0KCiMgVGhlIEZpdmUtTmFtZWQgR3JhcGhzIAoKIyMgVGhlIDVORyBvZiBkYXRhIHZpegoKLSBTY2F0dGVycGxvdDogYGdlb21fcG9pbnQoKWAKLSBMaW5lIGdyYXBoOiBgZ2VvbV9saW5lKClgCi0tCgotIEhpc3RvZ3JhbTogYGdlb21faGlzdG9ncmFtKClgCi0gQm94cGxvdDogYGdlb21fYm94cGxvdCgpYAotIEJhciBncmFwaDogYGdlb21fYmFyKClgCgoKLS0tCgpjbGFzczogY2VudGVyLCBtaWRkbGUKCiMjIE1vcmUgYGdncGxvdDJgIGV4YW1wbGVzCgotLS0KCiMjIEhpc3RvZ3JhbQoKYGBge3IgZmlnLmhlaWdodD01LjV9CmxpYnJhcnkobnljZmxpZ2h0czEzKQpnZ3Bsb3QoZGF0YSA9IHdlYXRoZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGh1bWlkKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKQpgYGAKCi0tLQoKIyMgQm94cGxvdCAoYnJva2VuKQoKYGBge3IgZmlnLmhlaWdodD01LjV9CmxpYnJhcnkobnljZmxpZ2h0czEzKQpnZ3Bsb3QoZGF0YSA9IHdlYXRoZXIsIG1hcHBpbmcgPSBhZXMoeCA9IG1vbnRoLCB5ID0gaHVtaWQpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgotLS0KCgojIyBCb3hwbG90IChhbG1vc3QgZml4ZWQpCgpgYGB7ciBmaWcuaGVpZ2h0PTUuNX0KbGlicmFyeShueWNmbGlnaHRzMTMpCmdncGxvdChkYXRhID0gd2VhdGhlciwgbWFwcGluZyA9IGFlcyh4ID0gbW9udGgsIGdyb3VwID0gbW9udGgsIHkgPSBodW1pZCkpICsKICBnZW9tX2JveHBsb3QoKSAKYGBgCi0tLQoKIyMgQm94cGxvdCAoZml4ZWQpCgpgYGB7ciBmaWcuaGVpZ2h0PTUuNX0KbGlicmFyeShueWNmbGlnaHRzMTMpCmdncGxvdChkYXRhID0gd2VhdGhlciwgbWFwcGluZyA9IGFlcyh4ID0gbW9udGgsIGdyb3VwID0gbW9udGgsIHkgPSBodW1pZCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6MTIpCmBgYAoKLS0tCgojIyBCYXIgZ3JhcGgKCmBgYHtyfQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCkKZ2dwbG90KGRhdGEgPSBiZWNoZGVsLCBtYXBwaW5nID0gYWVzKHggPSBjbGVhbl90ZXN0KSkgKwogIGdlb21fYmFyKCkKYGBgCgotLS0KCiMjIEhvdyBhYm91dCBvdmVyIHRpbWU/CgotIEhvcCBpbnRvIGBkcGx5cmAKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQp5ZWFyX2JpbnMgPC0gYygiJzcwLSc3NCIsICInNzUtJzc5IiwgIic4MC0nODQiLCAiJzg1LSc4OSIsCiAgICAgICAgICAgICAgICInOTAtJzk0IiwgIic5NS0nOTkiLCAiJzAwLScwNCIsICInMDUtJzA5IiwKICAgICAgICAgICAgICAgIicxMC0nMTMiKQpiZWNoZGVsIDwtIGJlY2hkZWwgJT4lCiAgbXV0YXRlKGZpdmVfeWVhciA9IGN1dCh5ZWFyLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgxOTY5LCAyMDE0LCA1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSB5ZWFyX2JpbnMpKSAlPiUgCiAgbXV0YXRlKGNsZWFuX3Rlc3QgPSBmYWN0b3IoY2xlYW5fdGVzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygibm93b21lbiIsICJub3RhbGsiLCAibWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkdWJpb3VzIiwgIm9rIikpKQpgYGAKCi0tLQoKIyMgSG93IGFib3V0IG92ZXIgdGltZT8gKFN0YWNrZWQpCgpgYGB7ciBmaWcud2lkdGg9MTF9CmxpYnJhcnkoZml2ZXRoaXJ0eWVpZ2h0KQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRhID0gYmVjaGRlbCwKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGZpdmVfeWVhciwgZmlsbCA9IGNsZWFuX3Rlc3QpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCi0tLQoKIyMgSG93IGFib3V0IG92ZXIgdGltZT8gKFNpZGUtYnktc2lkZSkKCmBgYHtyIGZpZy53aWR0aD0xMX0KbGlicmFyeShmaXZldGhpcnR5ZWlnaHQpCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBiZWNoZGVsLAogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZml2ZV95ZWFyLCBmaWxsID0gY2xlYW5fdGVzdCkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCmBgYAoKLS0tCgojIyBIb3cgYWJvdXQgb3ZlciB0aW1lPyAoU3RhY2tlZCBwcm9wb3J0aW9uYWwpCgpgYGB7ciBmaWcud2lkdGg9MTF9CmxpYnJhcnkoZml2ZXRoaXJ0eWVpZ2h0KQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRhID0gYmVjaGRlbCwKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGZpdmVfeWVhciwgZmlsbCA9IGNsZWFuX3Rlc3QpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIGNvbG9yID0gImJsYWNrIikKYGBgCgotLS0KCmNsYXNzOiBjZW50ZXIsIG1pZGRsZQoKIyMgVGhlIGB0aWR5dmVyc2VgL2BnZ3Bsb3QyYCBpcyBmb3IgYmVnaW5uZXJzIGFuZCA8YnI+IGZvciBkYXRhIHNjaWVuY2UgcHJvZmVzc2lvbmFscyEKCjxhIGhyZWY9Imh0dHBzOi8vZml2ZXRoaXJ0eWVpZ2h0LmNvbS9mZWF0dXJlcy90aGUtZG9sbGFyLWFuZC1jZW50cy1jYXNlLWFnYWluc3QtaG9sbHl3b29kcy1leGNsdXNpb24tb2Ytd29tZW4vIj48aW1nIHNyYz0iZmlndXJlL2JlY2hkZWwucG5nIiBzdHlsZT0id2lkdGg6IDUwMHB4OyIvPjxhPgoKLS0tCgojIyBQcmFjdGljZQoKUHJvZHVjZSBhcHByb3ByaWF0ZSA1Tkcgd2l0aCBSIHBhY2thZ2UgJiBkYXRhIHNldCBpbiBbIF0sIGUuZy4sIFtgbnljZmxpZ2h0czEzYCAkXHJpZ2h0YXJyb3ckIGB3ZWF0aGVyYF0gCgo8IS0tClRyeSB0byBsb29rIHRocm91Z2ggdGhlIGhlbHAgZG9jdW1lbnRhdGlvbi9Hb29nbGUgdG8gaW1wcm92ZSB5b3VyIHBsb3RzCi0tPgoKMS4gRG9lcyBgYWdlYCBwcmVkaWN0IGByZWNsaW5lX3J1ZGVgPyA8YnI+IFtgZml2ZXRoaXJ0eWVpZ2h0YCAkXHJpZ2h0YXJyb3ckIGBuYS5vbWl0KGZseWluZylgXQoKMi4gRGlzdHJpYnV0aW9uIG9mIGBhZ2VgIGJ5IGBzZXhgIDxicj4gW2Bva2N1cGlkZGF0YWAgJFxyaWdodGFycm93JCBgcHJvZmlsZXNgXQoKMy4gRG9lcyBgYnVkZ2V0YCBwcmVkaWN0IGByYXRpbmdgPyA8YnI+IFtgZ2dwbG90Mm1vdmllc2AgJFxyaWdodGFycm93JCBgbW92aWVzYF0KCjQuIERpc3RyaWJ1dGlvbiBvZiBsb2cgYmFzZSAxMCBzY2FsZSBvZiBgYnVkZ2V0XzIwMTNgIDxicj4gW2BmaXZldGhpcnR5ZWlnaHRgICRccmlnaHRhcnJvdyQgYGJlY2hkZWxgXQoKLS0tCgojIyMgSElOVFMKCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwLjV9CmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCkKbGlicmFyeShnZ3Bsb3QybW92aWVzKQpsaWJyYXJ5KG9rY3VwaWRkYXRhKQoKcDEgPC0gZ2dwbG90KGRhdGEgPSBuYS5vbWl0KGZseWluZyksIG1hcHBpbmcgPSBhZXMoZmlsbCA9IHJlY2xpbmVfcnVkZSwgeCA9IGFnZSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgZ2d0aXRsZSgiUHJvYmxlbSAxIikgKyB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDIwKQoKcDIgPC0gZ2dwbG90KGRhdGEgPSBwcm9maWxlcywgbWFwcGluZyA9IGFlcyh4ID0gc2V4LCB5ID0gYWdlKSkgKwogIGdlb21fYm94cGxvdCgpICsgZ2d0aXRsZSgiUHJvYmxlbSAyIikgKyB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDIwKQoKcDMgPC0gZ2dwbG90KGRhdGEgPSBtb3ZpZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGJ1ZGdldCwgeSA9IHJhdGluZykpICsKICBnZW9tX3BvaW50KCkgKyBnZ3RpdGxlKCJQcm9ibGVtIDMiKSArIHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMjApCgpwNCA8LSBnZ3Bsb3QoZGF0YSA9IGJlY2hkZWwsIG1hcHBpbmcgPSBhZXMoeCA9IGxvZyhidWRnZXRfMjAxMywgMTApKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gIndoaXRlIiwgYmlucyA9IDEwKSArIGdndGl0bGUoIlByb2JsZW0gNCIpICsKICB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDIwKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBuY29sID0gMiwgcGFkZGluZyA9IHVuaXQoMC41LCAibGluZSIpLAogICAgICAgICAgICAgd2lkdGhzID0gYygyLjYsIDEuNCkpCgpgYGAKCi0tLQoKY2xhc3M6IGludmVyc2UsIGNlbnRlciwgbWlkZGxlCgojIERFTU8gb2YgYGdncGxvdDJgIGluIFJTdHVkaW8KCi0tLQoKY2xhc3M6IGNlbnRlciwgbWlkZGxlCgojIyMgRGV0ZXJtaW5pbmcgdGhlIGFwcHJvcHJpYXRlIHBsb3QKCjxhIGhyZWY9Imh0dHBzOi8vY29nZ2xlLml0L2RpYWdyYW0vVl9HMmd6dWtURG9RLWFadCI+PGltZyBzcmM9ImZpZ3VyZS92aXpfbWluZG1hcC5wbmciIHN0eWxlPSJ3aWR0aDogNDAwcHg7Ii8+PGE+CgotLS0KCmNsYXNzOiBpbnZlcnNlLCBjZW50ZXIsIG1pZGRsZQpuYW1lOiB3cmFuZ2xlCgojIyBEYXkgMgoKIyMgRGF0YSBXcmFuZ2xpbmcKCi0tLQoKIyMjIGBnYXBtaW5kZXJgIGRhdGEgZnJhbWUgaW4gdGhlIGBnYXBtaW5kZXJgIHBhY2thZ2UKCmBgYHtyIHJvd3MucHJpbnQ9MTV9CmxpYnJhcnkoZ2FwbWluZGVyKQpnYXBtaW5kZXIKYGBgCgotLS0KCiMjIEJhc2UgUiB2ZXJzdXMgdGhlIGB0aWR5dmVyc2VgCgpTYXkgd2Ugd2FudGVkIG1lYW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyBhbGwgeWVhcnMgZm9yIEFzaWEKCi0tCgpgYGB7cn0KIyBCYXNlIFIKYXNpYSA8LSBnYXBtaW5kZXJbZ2FwbWluZGVyJGNvbnRpbmVudCA9PSAiQXNpYSIsIF0KbWVhbihhc2lhJGxpZmVFeHApCmBgYAotLQogCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnYXBtaW5kZXIgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gIkFzaWEiKSAlPiUKICBzdW1tYXJpemUobWVhbl9leHAgPSBtZWFuKGxpZmVFeHApKQpgYGAKCi0tLQoKIyMgVGhlIHBpcGUgYCU+JWAKCmByIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmUvcGlwZS5wbmciKWAgJmVtc3A7ICZlbXNwO2ByIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmUvTWFncml0dGVQaXBlLmpwZyIpYAotLQoKLSBBIHdheSB0byBjaGFpbiB0b2dldGhlciBjb21tYW5kcwotLQoKLSBJdCBpcyAqZXNzZW50aWFsbHkqIHRoZSBgZHBseXJgIGVxdWl2YWxlbnQgdG8gdGhlIDxicj4gYCtgIGluIGBnZ3Bsb3QyYAoKLS0tCgojIyBUaGUgNU5HIG9mIGRhdGEgdml6Ci0tCgojIyMgYGdlb21fcG9pbnQoKWA8YnI+IGBnZW9tX2xpbmUoKWAgPGJyPiBgZ2VvbV9oaXN0b2dyYW0oKWA8YnI+ICBgZ2VvbV9ib3hwbG90KClgPGJyPiBgZ2VvbV9iYXIoKWAKCi0tLQoKIyBUaGUgRml2ZSBNYWluIFZlcmJzICg1TVYpIG9mIGRhdGEgd3JhbmdsaW5nCgojIyMgYGZpbHRlcigpYCA8YnI+IGBzdW1tYXJpemUoKWAgPGJyPiBgZ3JvdXBfYnkoKWAgPGJyPiBgbXV0YXRlKClgIDxicj4gYGFycmFuZ2UoKWAKCi0tLQoKIyMgYGZpbHRlcigpYAoKLSBTZWxlY3QgYSBzdWJzZXQgb2YgdGhlIHJvd3Mgb2YgYSBkYXRhIGZyYW1lLiAKCi0gVGhlIGFyZ3VtZW50cyBhcmUgdGhlICJmaWx0ZXJzIiB0aGF0IHlvdSdkIGxpa2UgdG8gYXBwbHkuCi0tCgpgYGB7cn0KbGlicmFyeShnYXBtaW5kZXIpOyBsaWJyYXJ5KGRwbHlyKQpnYXBfMjAwNyA8LSBnYXBtaW5kZXIgJT4lIGZpbHRlcih5ZWFyID09IDIwMDcpCmhlYWQoZ2FwXzIwMDcpCmBgYAoKLSBVc2UgYD09YCB0byBjb21wYXJlIGEgdmFyaWFibGUgdG8gYSB2YWx1ZQoKLS0tCgojIyBMb2dpY2FsIG9wZXJhdG9ycwoKLSBVc2UgYHxgIHRvIGNoZWNrIGZvciBhbnkgaW4gbXVsdGlwbGUgZmlsdGVycyBiZWluZyB0cnVlOgotLQoKYGBge3IgZXZhbD1GQUxTRX0KZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAyMDAyIHwgY29udGluZW50ID09ICJFdXJvcGUiKQpgYGAKLS0KCmBgYHtyIGVjaG89RkFMU0V9CmdhcG1pbmRlciAlPiUgCiAgZmlsdGVyKHllYXIgPT0gMjAwMiB8IGNvbnRpbmVudCA9PSAiRXVyb3BlIikKYGBgCgotLS0KCiMjIExvZ2ljYWwgb3BlcmF0b3JzCgotIFVzZSBgJmAgb3IgYCxgIHRvIGNoZWNrIGZvciBhbGwgb2YgbXVsdGlwbGUgZmlsdGVycyBiZWluZyB0cnVlOgotLQoKYGBge3IgZXZhbD1GQUxTRX0KZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAyMDAyLCBjb250aW5lbnQgPT0gIkV1cm9wZSIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAyMDAyLCBjb250aW5lbnQgPT0gIkV1cm9wZSIpCmBgYAoKLS0tCgojIyBMb2dpY2FsIG9wZXJhdG9ycwoKLSBVc2UgYCVpbiVgIHRvIGNoZWNrIGZvciBhbnkgYmVpbmcgdHJ1ZSA8YnI+IChzaG9ydGN1dCB0byB1c2luZyBgfGAgcmVwZWF0ZWRseSB3aXRoIGA9PWApCi0tCgpgYGB7ciBldmFsPUZBTFNFfQpnYXBtaW5kZXIgJT4lIAogIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiQXJnZW50aW5hIiwgIkJlbGdpdW0iLCAiTWV4aWNvIiksCiAgICAgICAgIHllYXIgJWluJSBjKDE5ODcsIDE5OTIpKQpgYGAKLS0KCmBgYHtyIGVjaG89RkFMU0V9CmdhcG1pbmRlciAlPiUgCiAgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJBcmdlbnRpbmEiLCAiQmVsZ2l1bSIsICJNZXhpY28iKSwKICAgICAgICAgeWVhciAlaW4lIGMoMTk4NywgMTk5MikpCmBgYAoKCi0tLQoKIyMgYHN1bW1hcml6ZSgpYAoKLSBBbnkgbnVtZXJpY2FsIHN1bW1hcnkgdGhhdCB5b3Ugd2FudCB0byBhcHBseSB0byBhIGNvbHVtbiBvZiBhIGRhdGEgZnJhbWUgaXMgc3BlY2lmaWVkIHdpdGhpbiBgc3VtbWFyaXplKClgLgoKYGBge3IgZXZhbD1GQUxTRX0KbWF4X2V4cF8xOTk3IDwtIGdhcG1pbmRlciAlPiUgCiAgZmlsdGVyKHllYXIgPT0gMTk5NykgJT4lIAogIHN1bW1hcml6ZShtYXhfZXhwID0gbWF4KGxpZmVFeHApKQptYXhfZXhwXzE5OTcKYGBgCi0tCgpgYGB7ciBlY2hvPUZBTFNFfQptYXhfZXhwXzE5OTcgPC0gZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAxOTk3KSAlPiUgCiAgc3VtbWFyaXplKG1heF9leHAgPSBtYXgobGlmZUV4cCkpCm1heF9leHBfMTk5NwpgYGAKCgoKLS0tCgojIyMgQ29tYmluaW5nIGBzdW1tYXJpemUoKWAgd2l0aCBgZ3JvdXBfYnkoKWAKCldoZW4geW91J2QgbGlrZSB0byBkZXRlcm1pbmUgYSBudW1lcmljYWwgc3VtbWFyeSBmb3IgYWxsCmxldmVscyBvZiBhIGRpZmZlcmVudCBjYXRlZ29yaWNhbCB2YXJpYWJsZQoKYGBge3IgZXZhbD1GQUxTRX0KbWF4X2V4cF8xOTk3X2J5X2NvbnQgPC0gZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAxOTk3KSAlPiUgCiAgZ3JvdXBfYnkoY29udGluZW50KSAlPiUKICBzdW1tYXJpemUobWF4X2V4cCA9IG1heChsaWZlRXhwKSkKbWF4X2V4cF8xOTk3X2J5X2NvbnQKYGBgCgotLQpgYGB7ciBlY2hvPUZBTFNFfQptYXhfZXhwXzE5OTdfYnlfY29udCA8LSBnYXBtaW5kZXIgJT4lIAogIGZpbHRlcih5ZWFyID09IDE5OTcpICU+JSAKICBncm91cF9ieShjb250aW5lbnQpICU+JQogIHN1bW1hcml6ZShtYXhfZXhwID0gbWF4KGxpZmVFeHApKQptYXhfZXhwXzE5OTdfYnlfY29udApgYGAKCi0tLQoKIyMjIFdpdGhvdXQgdGhlIGAlPiVgCgpJdCdzIGhhcmQgdG8gYXBwcmVjaWF0ZSB0aGUgYCU+JWAgd2l0aG91dCBzZWVpbmcgd2hhdCB0aGUgY29kZSB3b3VsZCBsb29rIGxpa2Ugd2l0aG91dCBpdDoKCmBgYHtyfQptYXhfZXhwXzE5OTdfYnlfY29udCA8LSAKICBzdW1tYXJpemUoCiAgICBncm91cF9ieSgKICAgICAgZmlsdGVyKAogICAgICAgIGdhcG1pbmRlciwgCiAgICAgICAgICB5ZWFyID09IDE5OTcpLCAKICAgICAgY29udGluZW50KSwKICAgIG1heF9leHAgPSBtYXgobGlmZUV4cCkpCm1heF9leHBfMTk5N19ieV9jb250CmBgYAoKCgotLS0KCiMjIGBnZ3Bsb3QyYCByZXZpc2l0ZWQKCkZvciBhZ2dyZWdhdGVkIGRhdGEsIHVzZSBgZ2VvbV9jb2xgCgpgYGB7ciBmaWcuaGVpZ2h0PTQuNX0KZ2dwbG90KGRhdGEgPSBtYXhfZXhwXzE5OTdfYnlfY29udCwgCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBjb250aW5lbnQsIHkgPSBtYXhfZXhwKSkgKwogIGdlb21fY29sKCkKYGBgCi0tLQoKCiMjIFRoZSA1TVYKCi0gYGZpbHRlcigpYAotIGBzdW1tYXJpemUoKWAKLSBgZ3JvdXBfYnkoKWAKLS0KCi0gYG11dGF0ZSgpYAoKLS0KLSBgYXJyYW5nZSgpYAoKLS0tCgojIyBgbXV0YXRlKClgCgotIEFsbG93cyB5b3UgdG8gCiAgICAxLiA8Zm9udCBjb2xvcj0iYmx1ZSI+Y3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggYSBzcGVjaWZpYyB2YWx1ZTwvZm9udD4gT1IKICAgIDIuIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBiYXNlZCBvbiBvdGhlciB2YXJpYWJsZXMgT1IKICAgIDMuIGNoYW5nZSB0aGUgY29udGVudHMgb2YgYW4gZXhpc3RpbmcgdmFyaWFibGUKCi0tCgpgYGB7cn0KZ2FwX3BsdXMgPC0gZ2FwbWluZGVyICU+JSBtdXRhdGUoanVzdF9vbmUgPSAxKQpoZWFkKGdhcF9wbHVzKQpgYGAKCi0tLQoKIyMgYG11dGF0ZSgpYAoKLSBBbGxvd3MgeW91IHRvIAogICAgMS4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggYSBzcGVjaWZpYyB2YWx1ZSBPUgogICAgMi4gPGZvbnQgY29sb3I9ImJsdWUiPmNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBiYXNlZCBvbiBvdGhlciB2YXJpYWJsZXM8L2ZvbnQ+IE9SCiAgICAzLiBjaGFuZ2UgdGhlIGNvbnRlbnRzIG9mIGFuIGV4aXN0aW5nIHZhcmlhYmxlCgotLQoKYGBge3J9CmdhcF93X2dkcCA8LSBnYXBtaW5kZXIgJT4lIG11dGF0ZShnZHAgPSBwb3AgKiBnZHBQZXJjYXApCmhlYWQoZ2FwX3dfZ2RwKQpgYGAKCi0tLQoKIyMgYG11dGF0ZSgpYAoKLSBBbGxvd3MgeW91IHRvIAogICAgMS4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggYSBzcGVjaWZpYyB2YWx1ZSBPUgogICAgMi4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGJhc2VkIG9uIG90aGVyIHZhcmlhYmxlcyBPUgogICAgMy4gPGZvbnQgY29sb3I9ImJsdWUiPmNoYW5nZSB0aGUgY29udGVudHMgb2YgYW4gZXhpc3RpbmcgdmFyaWFibGU8L2ZvbnQ+CgotLQoKYGBge3J9CmdhcF93ZWlyZCA8LSBnYXBtaW5kZXIgJT4lIG11dGF0ZShwb3AgPSBwb3AgKyAxMDAwKQpoZWFkKGdhcF93ZWlyZCkKYGBgCgotLS0KCiMjIGBhcnJhbmdlKClgCgotIFJlb3JkZXJzIHRoZSByb3dzIGluIGEgZGF0YSBmcmFtZSBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIG9uZSBvciBtb3JlIHZhcmlhYmxlcwotLQoKYGBge3J9CmdhcG1pbmRlciAlPiUKICBhcnJhbmdlKHllYXIsIGNvdW50cnkpCmBgYAoKLS0tCgojIyBgYXJyYW5nZSgpYAoKLSBDYW4gYWxzbyBwdXQgaW50byBkZXNjZW5kaW5nIG9yZGVyCi0tCgpgYGB7ciBkZXNjfQpnYXBtaW5kZXIgJT4lCiAgZmlsdGVyKHllYXIgPiAyMDAwKSAlPiUKICBhcnJhbmdlKGRlc2MobGlmZUV4cCkpCmBgYAoKLS0tCgojIyBEb24ndCBtaXggdXAgYGFycmFuZ2VgIGFuZCBgZ3JvdXBfYnlgCgotIGBncm91cF9ieWAgaXMgdXNlZCAobW9zdGx5KSB3aXRoIGBzdW1tYXJpemVgIHRvIGNhbGN1bGF0ZSBzdW1tYXJpZXMgb3ZlciBncm91cHMKCi0gYGFycmFuZ2VgIGlzIHVzZWQgZm9yIHNvcnRpbmcKCi0tLQoKIyMgRG9uJ3QgbWl4IHVwIGBhcnJhbmdlYCBhbmQgYGdyb3VwX2J5YAoKVGhpcyBkb2Vzbid0IHJlYWxseSBkbyBhbnl0aGluZyB1c2VmdWwKCmBgYHtyIHJvd3MucHJpbnQ9MTB9CmdhcG1pbmRlciAlPiUgZ3JvdXBfYnkoeWVhcikKYGBgCgotLS0KCiMjIERvbid0IG1peCB1cCBgYXJyYW5nZWAgYW5kIGBncm91cF9ieWAKCkJ1dCB0aGlzIGRvZXMKCmBgYHtyIHJvd3MucHJpbnQ9MTB9CmdhcG1pbmRlciAlPiUgYXJyYW5nZSh5ZWFyKQpgYGAKCi0tLQoKIyMgQ2hhbmdpbmcgb2Ygb2JzZXJ2YXRpb24gdW5pdAoKVHJ1ZSBvciBGYWxzZQo+IEVhY2ggb2YgYGZpbHRlcmAsIGBtdXRhdGVgLCBhbmQgYGFycmFuZ2VgIGNoYW5nZSB0aGUgb2JzZXJ2YXRpb25hbCB1bml0LgoKLS0KClRydWUgb3IgRmFsc2UKPiBgZ3JvdXBfYnkoKSAlPiUgc3VtbWFyaXplKClgIGNoYW5nZXMgdGhlIG9ic2VydmF0aW9uYWwgdW5pdC4KCjwhLS0gCkRyYXcgZGlhZ3JhbSBmb3IgYXZlcmFnZSBtb250aGx5IHRlbXAgYWdncmVnYXRlZCBsaWtlIG9uIHJzdHVkaW86OmNvbmYgc2xpZGVzCi0tPgoKLS0tCgojIyBXaGF0IGlzIG1lYW50IGJ5ICJqb2luaW5nIGRhdGEgZnJhbWVzIiBhbmQgPGJyPiB3aHkgaXMgaXQgdXNlZnVsPwoKLS0KCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vaXNtYXljLmdpdGh1Yi5pby9tb2Rlcm5kaXZlci1ib29rL2ltYWdlcy9qb2luLWlubmVyLnBuZyIpCmBgYAoKLS0tCgojIyMgRG9lcyBjb3N0IG9mIGxpdmluZyBpbiBhIHN0YXRlIHJlbGF0ZSB0byB3aGV0aGVyIHBvbGljZSBvZmZpY2VycyBsaXZlIGluIHRoZSBjaXRpZXMgdGhleSBwYXRyb2w/ICBXaGF0IGFib3V0IHN0YXRlIHBvbGl0aWNhbCBpZGVvbG9neT8KCgpgYGB7ciBldmFsPUZBTFNFfQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCk7IGxpYnJhcnkocmVhZHIpOyBsaWJyYXJ5KGRwbHlyKQpwb2xpY2UyIDwtIHBvbGljZV9sb2NhbHMgJT4lIHNlbGVjdChjaXR5LCBhbGwpCmlkZW9sb2d5IDwtIHJlYWRfY3N2KCJodHRwczovL2lzbWF5Yy5naXRodWIuaW8vRWZmZWN0aXZlLURhdGEtU3Rvcnl0ZWxsaW5nLXVzaW5nLXRoZS10aWR5dmVyc2UvZGF0YXNldHMvaWRlb2xvZ3kuY3N2IikKcG9saWNlX2pvaW4gPC0gaW5uZXJfam9pbih4ID0gcG9saWNlMiwgeSA9IGlkZW9sb2d5LCBieSA9ICJjaXR5IikKcG9saWNlX2pvaW4KYGBgCgotLQoKYGBge3IgZWNobz1GQUxTRX0KbGlicmFyeShmaXZldGhpcnR5ZWlnaHQpOyBsaWJyYXJ5KHJlYWRyKTsgbGlicmFyeShkcGx5cikKcG9saWNlMiA8LSBwb2xpY2VfbG9jYWxzICU+JSBzZWxlY3QoY2l0eSwgYWxsKQppZGVvbG9neSA8LSByZWFkX2NzdigiaHR0cHM6Ly9pc21heWMuZ2l0aHViLmlvL0VmZmVjdGl2ZS1EYXRhLVN0b3J5dGVsbGluZy11c2luZy10aGUtdGlkeXZlcnNlL2RhdGFzZXRzL2lkZW9sb2d5LmNzdiIpCnBvbGljZV9qb2luIDwtIGlubmVyX2pvaW4oeCA9IHBvbGljZTIsIHkgPSBpZGVvbG9neSwgYnkgPSAiY2l0eSIpCnBvbGljZV9qb2luCmBgYAoKCi0tLQoKYGBge3J9CnVybCA8LSBwYXN0ZTAoImh0dHBzOi8vaXNtYXljLmdpdGh1Yi5pby8iLAogICAgICAgICAgICAgICJFZmZlY3RpdmUtRGF0YS1TdG9yeXRlbGxpbmctdXNpbmctdGhlLXRpZHl2ZXJzZS8iLAogICAgICAgICAgICAgICJkYXRhc2V0cy9jb3N0X29mX2xpdmluZy5jc3YiKQpjb3N0X29mX2xpdmluZyA8LSByZWFkX2Nzdih1cmwpCnBvbGljZV9qb2luX2Nvc3QgPC0gaW5uZXJfam9pbigKICAgIHggPSBwb2xpY2Vfam9pbiwgCiAgICB5ID0gY29zdF9vZl9saXZpbmcsIAogICAgYnkgPSAic3RhdGUiCiAgKQpwb2xpY2Vfam9pbl9jb3N0ICU+JSBzZWxlY3QoLXN0YXRlKSAlPiUgYXJyYW5nZShjaXR5KQpgYGAKCi0tLQoKIyMjIERvZXMgY29zdCBvZiBsaXZpbmcgaW4gYSBzdGF0ZSByZWxhdGUgdG8gd2hldGhlciBwb2xpY2Ugb2ZmaWNlcnMgbGl2ZSBpbiB0aGUgY2l0aWVzIHRoZXkgcGF0cm9sPyAgV2hhdCBhYm91dCBzdGF0ZSBwb2xpdGljYWwgaWRlb2xvZ3k/CgpgYGB7ciBmaWcud2lkdGg9MTB9CmdncGxvdChkYXRhID0gcG9saWNlX2pvaW5fY29zdCwKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGluZGV4LCB5ID0gYWxsKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IHN0YXRlX2lkZW9sb2d5KSkgKwogIGxhYnMoeCA9ICJDb3N0IG9mIExpdmluZyBJbmRleCIsIHkgPSAiJSBPZmZpY2VycyBMaXZpbmcgaW4gQ2l0eSIpCmBgYAoKLS0tCgojIyBQcmFjdGljZSA8c21hbGw+PHNtYWxsPihMYXkgb3V0IHdoYXQgdGhlIHJlc3VsdGluZyB0YWJsZSBzaG91bGQgbG9vayBsaWtlIG9uIHBhcGVyIGZpcnN0Lik8L3NtYWxsPjwvc21hbGw+CgpVc2UgdGhlIDVNViB0byBhbnN3ZXIgcHJvYmxlbXMgZnJvbSBSIGRhdGEgcGFja2FnZXMsIGUuZy4sIFtgbnljZmxpZ2h0czEzYCAkXHJpZ2h0YXJyb3ckIGB3ZWF0aGVyYF0gCgoxLiBXaGF0IGlzIHRoZSBtYXhpbXVtIGFycml2YWwgZGVsYXkgZm9yIGVhY2ggY2FycmllciBkZXBhcnRpbmcgSkZLPyBbYG55Y2ZsaWdodHMxM2AgJFxyaWdodGFycm93JCBgZmxpZ2h0c2BdCgoyLiBEZXRlcm1pbmUgdGhlIHRvcCBmaXZlIG1vdmllcyBpbiB0ZXJtcyBvZiBkb21lc3RpYyByZXR1cm4gb24gaW52ZXN0bWVudCBmb3IgMjAxMyBzY2FsZWQgZGF0YTxicj4gW2BmaXZldGhpcnR5ZWlnaHRgICRccmlnaHRhcnJvdyQgYGJlY2hkZWxgXQoKMy4gW0luY2x1ZGUgdGhlIG5hbWUgb2YgdGhlIGRlc3RpbmF0aW9uIGFpcnBvcnQgYXMgYSBjb2x1bW4gaW4gdGhlIGBmbGlnaHRzYCBkYXRhIGZyYW1lXShodHRwOi8vcjRkcy5oYWQuY28ubnovZGlhZ3JhbXMvcmVsYXRpb25hbC1ueWNmbGlnaHRzLnBuZykgPGJyPiBbYG55Y2ZsaWdodHMxM2AgJFxyaWdodGFycm93JCBgZmxpZ2h0c2AsIGBhaXJwb3J0c2BdCgotLS0KCgpjbGFzczogaW52ZXJzZSwgY2VudGVyLCBtaWRkbGUKCiMgREVNTyBvZiBgZHBseXJgIGluIFJTdHVkaW8KCi0tLQoKPCEtLQojIyBIb21ld29yayBmb3IgdG9tb3Jyb3cKCi0gUmV2aWV3IHRoZSBEYXRhIEltcG9ydCBjaGVhdHNoZWV0IGFuZCB1c2UgdGhlIGByZWFkcmAsIGByZWFkeGxgLCBhbmQgYGhhdmVuYCBwYWNrYWdlcyB0byByZWFkIGluIGRhdGEgZnJvbSBDU1ZzLCBFeGNlbCBYTFMvWExTWCBmaWxlcywgU1RBVEEvU1BTUyBmaWxlcwoKLSBVc2UgYGdncGxvdDJgIGFuZCBgZHBseXJgIHRvIG1ha2UgZ3JhcGhpY3MgYW5kIHByb2R1Y2UgZGF0YSB3cmFuZ2xpbmcgb24gZGF0YSBzZXRzIG9mIGludGVyZXN0IHRvIHlvdSByZWFkIGluIGZyb20gdGhlIHByZXZpb3VzIHN0ZXAKLS0+CgpjbGFzczogaW52ZXJzZSwgY2VudGVyLCBtaWRkbGUKCgojIERhdGEgSW1wb3J0aW5nIGFuZCBUaWR5aW5nCgotLS0KCiMjIGB0aWR5dmVyc2VgIHBhY2thZ2VzCgoqKklNUE9SVCoqCgotIGBoYXZlbmAgZm9yIFNQU1MsIFNBUywgYW5kIFN0YXRhIGRhdGEgZmlsZXMKLSBganNvbmxpdGVgIGZvciBKU09OIGZpbGVzCi0gYHJlYWR4bGAgZm9yIFhMUyBhbmQgWExTWCBmaWxlcwotIGByZWFkcmAgZm9yIENTViBhbmQgVFNWIGZpbGVzIChhbmQgUiBzcGVjaWZpYyBSRFMgZmlsZXMpCgo8YnI+CgoqKlRJRFlJTkcqKgoKLSBgdGlkeXJgIGZvciBjb252ZXJ0aW5nICJtZXNzeSIgaW50byAidGlkeSIgZGF0YSBmcmFtZXMKCi0tLQoKbmFtZTogaW1wb3J0CgojIyBCYXNpY3Mgb2YgSW1wb3J0aW5nCgo8c21hbGw+V2Ugd2lsbCBiZWdpbiBieSBkb3dubG9hZGluZyBhbmQgaW1wb3J0aW5nIGEgdmFyaWV0eSBvZiBkaWZmZXJlbnQgIm1lc3N5IiBkYXRhIHNldHMuICBZb3UgY2FuIGRvd25sb2FkIGFsbCBvZiB0aGVtIGluIGEgWklQIGZpbGUgYXQgPGh0dHA6Ly9iaXQubHkvcmVlZC1tZXNzeS1kYXRhPi4gIFRoZSBsaW5rcyBiZWxvdyBnbyB0byB0aGUgb3JpZ2luYWwgc291cmNlcy4gIEkndmUgY29udmVydGVkIHRoZXNlIG9yaWdpbmFsIHNvdXJjZXMgaW50byBkaWZmZXJlbnQgZm9ybWF0cy48L3NtYWxsPgoKCi0gW0xpZmUgRXhwZWN0YW5jeSBieSB5ZWFyIGZvciBjb3VudHJpZXMgc2luY2UgMTk1MSAoQ1NWKV0oaHR0cHM6Ly9zcHJlYWRzaGVldHMuZ29vZ2xlLmNvbS9wdWI/a2V5PXBoQXdjTkFWdXlqMnRQTHhLdnZuTlBBJm91dHB1dD14bHMpCgotIFtXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIFRCIGRhdGEgKFN0YXRhIERUQSldKGh0dHA6Ly93d3cud2hvLmludC90Yi9jb3VudHJ5L2RhdGEvZG93bmxvYWQvZW4vKQoKLSBbQW5udWFsIEVzdGltYXRlcyBvZiBQb3B1bGF0aW9uIGJ5IEFnZSwgU2V4LCBSYWNlLCBhbmQgSGlzcGFuaWMgT3JpZ2luIGZvciBPcmVnb24gQ291bnRpZXMgKFhMU1gpXShodHRwczovL3d3dy5jZW5zdXMuZ292L3BvcGVzdC9kYXRhL2NvdW50aWVzL2FzcmgvMjAxMS9DQy1FU1QyMDExLWFsbGRhdGEuaHRtbCkKLSBbRWR1Y2F0aW9uYWwgYXR0YWlubWVudCBmb3IgdGhlIFUuUy4sIFN0YXRlcywgYW5kIGNvdW50aWVzLCAxOTcwLTIwMTQgKEpTT04pXShodHRwOi8vd3d3LmVycy51c2RhLmdvdi9kYXRhLXByb2R1Y3RzL2NvdW50eS1sZXZlbC1kYXRhLXNldHMvZG93bmxvYWQtZGF0YS5hc3B4KQotIFtDb3VudHkgbGV2ZWwgcmVzdWx0cyBmb3IgMjAxMiBQT1RVUyBlbGVjdGlvbiBmcm9tIFRoZSBHdWFyZGlhbiAoU1BTUyBTQVYpXShodHRwczovL2Z1c2lvbnRhYmxlcy5nb29nbGUuY29tL0RhdGFTb3VyY2U/ZG9jaWQ9MXFjUUxxckFJQWUzUmNFZmRXU21fUWNYTUxtdGVWZzR1U3BTczFyTSkKCgotLS0KCmNsYXNzOiBjZW50ZXIsIG1pZGRsZSwgaW52ZXJzZQoKIyMgRGVtb25zdHJhdGlvbiBpbiBSU3R1ZGlvCgotLS0KCiMjIFByYWN0aWNlCgotIERvd25sb2FkIHRoZSBmb3VyIG90aGVyIGZpbGVzIGFuZCBpbXBvcnQgdGhlbSBpbnRvIFIgdXNpbmcgdGhlIGFwcHJvcHJpYXRlIHBhY2thZ2UKICAgIC0gWW91IG1heSBuZWVkIHRvIGNoZWNrIG91dCB0aGUgaGVscCBwYWdlcyBmb3IgdGhlIGRpZmZlcmVudCBwYWNrYWdlcwogICAgICAgIC0gW2BoYXZlbmAgZm9yIFNQU1MsIFNBUywgYW5kIFN0YXRhIGRhdGEgZmlsZXNdKGh0dHA6Ly9oYXZlbi50aWR5dmVyc2Uub3JnLykKICAgICAgICAtIFtganNvbmxpdGVgIGZvciBKU09OIGZpbGVzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvanNvbmxpdGUvdmlnbmV0dGVzL2pzb24tYWFxdWlja3N0YXJ0Lmh0bWwpCiAgICAgICAgLSBbYHJlYWR4bGAgZm9yIFhMUyBhbmQgWExTWCBmaWxlc10oaHR0cHM6Ly9naXRodWIuY29tL2hhZGxleS9yZWFkeGwpCiAgICAgICAgLSBbYHJlYWRyYCBmb3IgQ1NWL1RTViBmaWxlcyAoJiBSIHNwZWNpZmljIFJEUyBmaWxlcyldKGh0dHA6Ly9yZWFkci50aWR5dmVyc2Uub3JnLykKICAgIC0gR2l2ZSB0aGVtIHRoZSBmb2xsb3dpbmcgbmFtZXM6IGB3aG9fZGZgLCBgY291bnR5X3BvcF9kZmAsIGBlZHVfY291bnR5X2RmYCwgYW5kIGBwb3R1czEyX2RmYAogIAotLS0KCm5hbWU6IHRpZHkKY2xhc3M6IGludmVyc2UsIG1pZGRsZSwgY2VudGVyCgojIFRpZHkgRGF0YQoKCi0tLQoKPGltZyBzcmM9Imh0dHA6Ly9nYXJyZXR0Z21hbi5naXRodWIuaW8vaW1hZ2VzL3RpZHktMS5wbmciIHN0eWxlPSJ3aWR0aDogNzUwcHg7Ii8+CgoxLiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLgoyLiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93LgozLiBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUuCgpUaGUgdGhpcmQgcG9pbnQgbWVhbnMgd2UgZG9uJ3QgbWl4IGFwcGxlcyBhbmQgb3Jhbmdlcy4KCi0tLQoKIyMgV2hhdCBpcyBUaWR5IERhdGE/CgoxLiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93LiBJbiBvdGhlciB3b3JkcywgZWFjaCByb3cgY29ycmVzcG9uZHMgdG8gYSBzaW5nbGUgaW5zdGFuY2Ugb2YgYW4gPHU+b2JzZXJ2YXRpb25hbCB1bml0PC91PgoxLiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uOgogICAgKyBTb21lIHZhcmlhYmxlcyBtYXkgYmUgdXNlZCB0byBpZGVudGlmeSB0aGUgPHU+b2JzZXJ2YXRpb25hbCB1bml0czwvdT4uIAogICAgKyBGb3Igb3JnYW5pemF0aW9uYWwgcHVycG9zZXMsIGl0J3MgZ2VuZXJhbGx5IGJldHRlciB0byBwdXQgdGhlc2UgaW4gdGhlIGxlZnQtaGFuZCBjb2x1bW5zCjEuIEVhY2ggdHlwZSBvZiBvYnNlcnZhdGlvbmFsIHVuaXQgZm9ybXMgYSB0YWJsZSB3aXRoIHRoZSBlbnRyaWVzIGluIHRoZSB0YWJsZSBjb3JyZXNwb25kaW5nIHRvIHZhbHVlcy4KCi0tLQoKIyMgRGlmZmVyZW50aWF0aW5nIGJldHdlZW4gPHU+bmVhdDwvdT4gZGF0YSBhbmQgPHU+dGlkeTwvdT4gZGF0YQoKLSBDb2xsb3F1aWFsbHksIHRoZXkgbWVhbiB0aGUgc2FtZSB0aGluZwotIEJ1dCBpbiBvdXIgY29udGV4dCwgb25lIGlzIGEgc3Vic2V0IG9mIHRoZSBvdGhlci4gCgo8YnI+Cgo8dT5OZWF0PC91PiBkYXRhIGlzIAogIC0gZWFzeSB0byBsb29rIGF0LCAKICAtIG9yZ2FuaXplZCBuaWNlbHksIGFuZCAKICAtIGluIHRhYmxlIGZvcm0uCgotLQoKPHU+VGlkeTwvdT4gZGF0YSBpcyBuZWF0IGJ1dCBhbHNvIGFiaWRlcyBieSBhIHNldCBvZiB0aHJlZSBydWxlcy4KCi0tLQoKY2xhc3M6IGNlbnRlciwgbWlkZGxlCgo8YSBocmVmPSJodHRwOi8vc3RyZWFtMS5naWZzb3VwLmNvbS92aWV3OC8yMDE1MDQwNC81MTkyODU5L2xlYm93c2tpLWFiaWRlcy1vLmdpZiI+PGltZyBzcmM9ImZpZ3VyZS9sZWJvd3NraS1hYmlkZXMtby5naWYiIHN0eWxlPSJ3aWR0aDogNDUwcHg7Ii8+PC9hPgoKCjxpbWcgc3JjPSJmaWd1cmUvdGlkeS0xLnBuZyIgYWx0PSJEcmF3aW5nIiBzdHlsZT0id2lkdGg6IDc1MHB4OyIvPgoKLS0tCgojIyBJcyB0aGlzIHRpZHk/CgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCkKc2V0LnNlZWQoMikKYmVjaGRlbCAlPiUgc2FtcGxlX24oMTIpICU+JQogIHNlbGVjdCh5ZWFyLCB0aXRsZSwgY2xlYW5fdGVzdCwgYnVkZ2V0XzIwMTMpICU+JQogIGFycmFuZ2UodGl0bGUpICU+JQogIGthYmxlKGZvcm1hdCA9ICJodG1sIikKYGBgCgoKLS0tCgpuYW1lOiBkZW1zY29yZQoKIyMgSG93IGFib3V0IHRoaXM/IElzIHRoaXMgdGlkeT8KCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRlbV9zY29yZSA8LSByZWFkX2NzdigiZGF0YS9kZW1fc2NvcmUuY3N2IikKa25pdHI6OmthYmxlKGRlbV9zY29yZSAlPiUgc2xpY2UoMToxMiksIGZvcm1hdCA9ICJodG1sIikKYGBgCgotLS0KCm5hbWU6IHdoeXRpZHkKCiMjIFdoeSBpcyB0aWR5IGRhdGEgaW1wb3J0YW50PwoKLSBUaGluayBhYm91dCB0cnlpbmcgdG8gcGxvdCBkZW1vY3JhY3kgc2NvcmUgYWNyb3NzIHllYXJzIGluIHRoZSBzaW1wbGVzdCB3YXkgcG9zc2libGUuCi0tCgotIEl0IHdvdWxkIGJlIG11Y2ggZWFzaWVyIGlmIHRoZSBkYXRhIGxvb2tlZCBsaWtlIHdoYXQgZm9sbG93cyBpbnN0ZWFkIHNvIHdlIGNvdWxkIHB1dCAKICAgIC0gYHllYXJgIG9uIHRoZSBgeGAtYXhpcyBhbmQgCiAgICAtIGBkZW1fc2NvcmVgIG9uIHRoZSBgeWAtYXhpcy4KCi0tLQoKIyMgVGlkeSBpcyBnb29kCgpgYGB7ciBlY2hvPUZBTFNFfQpkZW1fc2NvcmVfdGlkeSA8LSBkZW1fc2NvcmUgJT4lIAogIGdhdGhlcigtY291bnRyeSwga2V5ID0gInllYXIiLCB2YWx1ZSA9ICJkZW1fc2NvcmUiKSAlPiUgCiAgbXV0YXRlKHllYXIgPSBhcy5udW1lcmljKHllYXIpKSAKc2V0LnNlZWQoMikKZGVtX3Njb3JlX3RpZHkgJT4lIHNhbXBsZV9uKDEzKSAlPiUgYXJyYW5nZShjb3VudHJ5KSAlPiUgCiAga25pdHI6OmthYmxlKGZvcm1hdCA9ICJodG1sIikKYGBgCgotLS0KCiMjIExldCdzIHBsb3QgaXQKCi0gUGxvdCB0aGUgbGluZSBncmFwaCBmb3IgdGhyZWUgY291bnRyaWVzIHVzaW5nIGBnZ3Bsb3RgCgpgYGB7cn0KZGVtX3Njb3JlNCA8LSBkZW1fc2NvcmVfdGlkeSAlPiUKICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIlBha2lzdGFuIiwgIlBvcnR1Z2FsIiwgIlVydWd1YXkiKSkKZ2dwbG90KGRhdGEgPSBkZW1fc2NvcmU0LCBtYXBwaW5nID0gYWVzKHggPSB5ZWFyLCB5ID0gZGVtX3Njb3JlKSkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG9yID0gY291bnRyeSkpCmBgYAoKLS0tCgojIyBUaWR5aW5nCgojIyMgVGhlIExpZmUgRXhwZWN0YW5jeSBieSB5ZWFyIGRhdGEKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWZlX2V4cF9kZiA8LSByZWFkX2NzdigiZGF0YS9sZV9tZXNzLmNzdiIpCiNWaWV3KGxpZmVfZXhwX2RmKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmUvbGVfbWVzcy5wbmciKQpgYGAKCi0tLQoKIyMgRG9pbmcgdGhlICJ0aWR5aW5nIi9yZXNoYXBpbmcKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCihsaWZlX2V4cF90aWR5IDwtIGxpZmVfZXhwX2RmICU+JSAKICAgIGdhdGhlcihrZXkgPSAieWVhciIsIHZhbHVlID0gImxpZmVfZXhwIiwgLWNvdW50cnkpKQpgYGAKCi0tLQoKIyMgVGlkeWluZwoKIyMjIyBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIFRCIGRhdGEgKFN0YXRhIERUQSkKCgpgYGB7cn0KbGlicmFyeShoYXZlbikKd2hvX2RmIDwtIHJlYWRfZHRhKCJkYXRhL3doby5kdGEiKQojVmlldyh3aG9fZGYpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZS93aG9fbWVzcy5wbmciKQpgYGAKCi0tLQoKIyMgV0hPIHVnbHkuLi4KCi0gVGhpcyBkYXRhIHNldCBjb250YWlucyBzdHJhbmdlIHZhcmlhYmxlIG5hbWVzIHRoYXQgd2lsbCByZXF1aXJlIHVzIHRvIGxvb2sgdXAgdGhlaXIgbWVhbmluZyBpbiB0aGUgY29ycmVzcG9uZGluZyBbKipkYXRhIGRpY3Rpb25hcnkqKl0oaHR0cHM6Ly9leHRyYW5ldC53aG8uaW50L3RtZS9nZW5lcmF0ZUNTVi5hc3A/ZHM9ZGljdGlvbmFyeSkuCgotLQoKLSBXaGF0IGRvIHdlIGtub3c/CiAgICAgLSBgY291bnRyeWAsIGBpc28yYCwgYW5kIGBpc28zYCBhcmUgcmVkdW5kYW50IGFuZCBpZGVudGlmeSB0aGUgY291bnRyeQogICAgIC0gYHllYXJgIGlzIGEgdmFyaWFibGUgYW5kIGl0IGxvb2tzIGxpa2UgaXQgY29ycmVzcG9uZHMgdG8gZWFjaCBjb3VudHJ5Ci0gQnV0IHdoYXQgaW4gdGhlIHdvcmxkIGlzIGBuZXdfc3BfbTAxNGA/IEFuZCB0aGUgb3RoZXIgY29sdW1ucz8uLi4KCi0tLQoKIyMgRmlyc3Qgc3RlcAoKTGlrZSBiZWZvcmUsIHdlIGNhbiBgZ2F0aGVyYCB0aGVzZSBub24tYGNvdW50cnlgIGFuZCBub24tYHllYXJgIHZhcmlhYmxlcyB0b2dldGhlcjoKCmBgYHtyfQp3aG9fdGlkeSA8LSB3aG9fZGYgJT4lIAogIGdhdGhlcihuZXdfc3BfbTAxNDpuZXdyZWxfZjY1LCBrZXkgPSAia2V5IiwgdmFsdWUgPSAidmFsdWUiKQpgYGAKCldlIGNhbiBub3cgc2VlIHdoYXQgdGhpcyBoYXMgZG9uZSB0byB0aGUgZGF0YSBmcmFtZToKCmBgYHtyIGV2YWw9RkFMU0V9ClZpZXcod2hvX3RpZHkpCmBgYAoKLS0tCgojIyBEYXRhIGRpY3Rpb25hcnkgc2F2ZXMgdXMgc29tZS4uLgoKMS4gVGhlIGZpcnN0IHRocmVlIGxldHRlcnMgb2YgZW50cmllcyBpbiB0aGUgYGtleWAgY29sdW1uIGNvcnJlc3BvbmQgdG8gYG5ld2Agb3IgYG9sZGAgY2FzZXMgb2YgVEIuCjIuIFRoZSBuZXh0IHR3byBsZXR0ZXJzIChhZnRlciB0aGUgYF9gKSBjb3JyZXNwb25kIHRvIFRCIHR5cGU6CiAgICAtIGByZWxgIGZvciByZWxhcHNlLCBgZXBgIGZvciBleHRyYXB1bG1vbmFyeSBUQgogICAgLSBgc25gIGZvciBzbWVhciBuZWdhdGl2ZSwgYHNwYCBmb3Igc21lYXIgcG9zaXRpdmUKMy4gVGhlIG5leHQgbGV0dGVyIGFmdGVyIHRoZSBzZWNvbmQgYF9gIGNvcnJlc3BvbmRzIHRvIHRoZSBzZXggb2YgdGhlIFRCIHBhdGllbnQuCjQuIFRoZSByZW1haW5pbmcgbnVtYmVycyBjb3JyZXNwb25kIHRvIGFnZSBncm91cDoKICAgIC0gYDAxNGAgZm9yIDAgdG8gMTQgeWVhcnMKICAgIC0gLi4uCiAgICAtIGA2NWAgZm9yIDY1IG9yIG9sZGVyCgotLS0KCi0gTG9va2luZyBvdmVyIHRoZSBlbnRyaWVzIG9mIGBrZXlgIGluIGB3aG9fdGlkeWAsIGRvIHlvdSBzZWUgYW55dGhpbmcgZWxzZSB0aGF0IGRvZXNuJ3QgbWF0Y2ggdGhlIHBhdHRlcm4/CgotIEl0IG1heSBiZSBlYXNpZXIgdG8gcmVtZW1iZXIgdGhhdCB0aGUgZW50cmllcyBvZiBga2V5YCBjb3JyZXNwb25kIHRvIGNvbHVtbiBuYW1lcyBpbiBgd2hvX2RmYDoKCmBgYHtyfQpuYW1lcyh3aG9fZGYpCmBgYAoKLS0tCgojIyBBIGZpeCB1c2luZyBgc3RyaW5ncmAKCllvdSBjYW4gcmVwbGFjZSBhbGwgb2YgdGhlIGVudHJpZXMgaW4gYGtleWAgdGhhdCBjb250YWluZWQgYG5ld3JlbGAgd2l0aCBgbmV3X3JlbGAgdmlhCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQp3aG9fdGlkeSA8LSB3aG9fdGlkeSAlPiUgCiAgbXV0YXRlKGtleSA9IHN0cl9yZXBsYWNlKAogICAgICBzdHJpbmcgPSBrZXksIAogICAgICBwYXR0ZXJuID0gIm5ld3JlbCIsIAogICAgICByZXBsYWNlbWVudCA9ICJuZXdfcmVsIikKICAgICkKYGBgCgotLS0KCiMjIFB1bGxpbmcgYXBhcnQgdmFyaWFibGVzCgpUaGUgZW50cnkgYG5ld19zcF9tMDE0YCBpcyBhY3R1YWxseSBmb3VyIGRpZmZlcmVudCB2YXJpYWJsZXMuICBVc2UgdGhlIGBzZXBhcmF0ZWAgZnVuY3Rpb24gdG8gcHVsbCB0aGVtIGFwYXJ0OgoKYGBge3J9Cndob190aWR5IDwtIHdob190aWR5ICU+JSAKICBzZXBhcmF0ZShjb2wgPSBrZXksIGludG8gPSBjKCJuZXciLCAidHlwZSIsICJzZXhhZ2UiKSwgc2VwID0gIl8iKQp3aG9fdGlkeQpgYGAKCi0tLQoKIyMgT25lIG1vcmUgcHVsbCBhcGFydAoKLSBXZSBuZWVkIHRvIHB1bGwgYHNleGFnZWAgaW50byB0d28gZGlmZmVyZW50IHZhcmlhYmxlcy4gIAotIElmIHlvdSB1c2UgYD9zZXBhcmF0ZWAsIHlvdSdsbCBzZWUgdGhhdCB0aGUgZm9sbG93aW5nIGlzIGFuIG9wdGlvbjoKCmBgYHtyfQood2hvX3RpZHkgPC0gd2hvX3RpZHkgJT4lIAogIHNlcGFyYXRlKGNvbCA9IHNleGFnZSwgaW50byA9IGMoInNleCIsICJhZ2UiKSwgc2VwID0gMSkpCmBgYAoKLS0tCgojIyBGaW5hbCBjbGVhbmluZwoKLSBgaXNvMmAgYW5kIGBpc28zYCBhcmUgcmVkdW5kYW50IGlmIHdlIGhhdmUgYGNvdW50cnlgCi0gYG5ld2AgaXMgY29uc3RhbnQgc2luY2UgdGhpcyBvbmx5IGhhcyBuZXcgY2FzZXMgb2YgVEIKLSBMZXQncyBqdXN0IGlnbm9yZSBtaXNzaW5nIHZhbHVlcyBoZXJlCi0gV2UgYWxzbyBrbm93IHRoYXQgYHZhbHVlYCBpcyBhY3R1YWxseSBgY2FzZXNgIHNvIHdlIGNhbiBgcmVuYW1lYCB0aGF0IGNvbHVtbi4KCmBgYHtyLCB0aWR5PUZBTFNFfQp3aG9fdGlkeSA8LSB3aG9fdGlkeSAlPiUgc2VsZWN0KC1pc28yLCAtaXNvMywgLW5ldykgJT4lIAogIG5hLm9taXQoKSAlPiUgcmVuYW1lKCJjYXNlcyIgPSB2YWx1ZSkKaGVhZCh3aG9fdGlkeSwgNSkKYGBgCgotLS0KCiMjIFRoZSBwb3dlciBvZiB0aGUgYCU+JWAgKHBpcGUpCgpFdmVyeXRoaW5nIHdlIGRpZCBhYm92ZSBjYW4gYmUgY2hhaW5lZCBpbnRvIG9uZSBzZXF1ZW5jZToKCgpgYGB7ciBldmFsPUZBTFNFfQp3aG9fdGlkeSA8LSB3aG9fZGYgJT4lIAogIGdhdGhlcihuZXdfc3BfbTAxNDpuZXdyZWxfZjY1LCBrZXkgPSAia2V5IiwgdmFsdWUgPSAidmFsdWUiKSAlPiUgIAogIG11dGF0ZShrZXkgPSBzdHJfcmVwbGFjZShrZXksIHBhdHRlcm4gPSAibmV3cmVsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIm5ld19yZWwiKSkgJT4lIAogIHNlcGFyYXRlKGNvbCA9IGtleSwgaW50byA9IGMoIm5ldyIsICJ0eXBlIiwgInNleGFnZSIpLCBzZXAgPSAiXyIpICU+JSAKICBzZXBhcmF0ZShjb2wgPSBzZXhhZ2UsIGludG8gPSBjKCJzZXgiLCAiYWdlIiksIHNlcCA9IDEpICU+JSAKICBzZWxlY3QoLWlzbzIsIC1pc28zLCAtbmV3KSAlPiUgCiAgbmEub21pdCgpICU+JSAKICByZW5hbWUoImNhc2VzIiA9IHZhbHVlKQpgYGAKCi0tLQoKIyMgQk9OVVMKCiMjIyBBIEdhcG1pbmRlciB0aWR5IGRhdGFzZXQgcmVhZCBpbiBmcm9tIGEgR29vZ2xlIFNoZWV0IQoKLSBJJ3ZlIHVwZGF0ZWQgdGhlIGBnYXBtaW5kZXJgIGRhdGEgc2V0IGF2YWlsYWJsZSBpbiB0aGUgYGdhcG1pbmRlcmAgUiBkYXRhIHBhY2thZ2UgYnkgSmVubnkgQnJ5YW4gW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2dhcG1pbmRlci8pLiAgSmVubnkgcHJvdmlkZXMgW2luc3RydWN0aW9uc10oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvZ2FwbWluZGVyL3RyZWUvbWFzdGVyL2RhdGEtcmF3KSBmb3IgcmVjcmVhdGluZyB0aGUgZGF0YS4KCi0tLQoKIyMgQk9OVVMKCi0gWW91IGNhbiBkb3dubG9hZCB0aGUgdXBkYXRlZCBkYXRhIGZyb20gR29vZ2xlIFNoZWV0cyBieSBydW5uaW5nIHRoZSBmb2xsb3dpbmcgaW4gUjoKCmBgYHtyIGV2YWw9RkFMU0V9CiMgTGluayBpcyBodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xOEw1WmlYZDFDUTk3WFdTcWIwNHgxWVE0bmNzRU9kUjdlSHpnWDBaSXVQQS9lZGl0P3VzcD1zaGFyaW5nCmxpYnJhcnkoZ29vZ2xlc2hlZXRzKQp1cGRhdGVkX2dhcCA8LSBnc19rZXkoIjE4TDVaaVhkMUNROTdYV1NxYjA0eDFZUTRuY3NFT2RSN2VIemdYMFpJdVBBIikgJT4lCiAgZ3NfcmVhZCgpCmBgYAoKCi0gQSBzY3JpcHQgZ29pbmcgdGhyb3VnaCB0aGUgc3RlcHMgdG8gdGFrZSB0aGUgIm1lc3N5IiBvcmlnaW5hbCBHYXBtaW5kZXIub3JnIGZpbGVzIGFuZCB0dXJuIHRoZW0gaW50byB0aGlzIHRpZHkgZGF0YXNldCBpcyBhdmFpbGFibGUgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9pc21heWMvdGlkeXZlcnNlX3dvcmtzaG9wcy9tYXN0ZXIvaW1wb3J0aW5nX3RpZHlpbmcvZGF0YS9jbGVhbmluZy5SKS4KCi0tLQoKIyMgUHJhY3RpY2UgKGFmdGVyIHRoZSBib290Y2FtcCkKClRyeSB0byB0aWR5IHRoZSBvdGhlciBkYXRhIHNldHMgeW91IGRvd25sb2FkZWQgYW5kIGltcG9ydGVkIG9yIG90aGVyIGRhdGEgc2V0cyB5b3UgaGF2ZSEKCi0tLQoKbmFtZTogbW9kZWwKY2xhc3M6IGludmVyc2UsIGNlbnRlciwgbWlkZGxlCgojIERhdGEgTW9kZWxpbmcKCgotLS0KCgojIyBNb2RlbGluZwoKMS4gRXhwZXJpZW5jZSB3aXRoIGBnZ3Bsb3QyYCBwYWNrYWdlIGFuZCBrbm93bGVkZ2Ugb2YgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgcHJpbWVzIHN0dWRlbnRzIGZvciBtb2RlbGluZwoxLiBVc2Ugb2YgdGhlIGBicm9vbWAgcGFja2FnZSB0byB1bnBhY2sgcmVncmVzc2lvbiBvdXRwdXQgaW50byBhIHRpZHkgZm9ybWF0CgotLS0KCiMjIDEuIGBnZ3Bsb3QyYCBQcmltZXMgUmVncmVzc2lvbgoKRXhhbXBsZToKCiogQWxsIEFsYXNrYW4gQWlybGluZXMgYW5kIEZyb250aWVyIGZsaWdodHMgbGVhdmluZyBOWUMgaW4gMjAxMwoqIFdlIHdhbnQgdG8gc3R1ZHkgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCBkZXBhcnR1cmUgZGVsYXkKKiBGb3Igc3VtbWVyIChKdW5lLCBKdWx5LCBBdWd1c3QpIGFuZCBub24tc3VtbWVyIG1vbnRocyBzZXBhcmF0ZWx5CgpJbnZvbHZlcyBmb3VyIHZhcmlhYmxlczogCgotIGBjYXJyaWVyYCwgYHRlbXBgLCBgZGVwX2RlbGF5YCwgYHN1bW1lcmAKCi0tLQoKIyMgMS4gYGdncGxvdDJgIFByaW1lcyBSZWdyZXNzaW9uCgpgYGB7cn0KZmxpZ2h0c19zdWJzZXQgPC0gZmxpZ2h0cyAlPiUgCiAgZmlsdGVyKGNhcnJpZXIgPT0gIkFTIiB8IGNhcnJpZXIgPT0gIkY5IikgJT4lIAogIGxlZnRfam9pbih3ZWF0aGVyLCBieT1jKCJ5ZWFyIiwgIm1vbnRoIiwgImRheSIsICJob3VyIiwgIm9yaWdpbiIpKSAlPiUgCiAgZmlsdGVyKGRlcF9kZWxheSA8IDI1MCkgJT4lIAogIG11dGF0ZShzdW1tZXIgPSBpZmVsc2UobW9udGggPT0gNiB8IG1vbnRoID09IDcgfCBtb250aCA9PSA4LCAiU3VtbWVyIEZsaWdodHMiLCAiTm9uLVN1bW1lciBGbGlnaHRzIikpCmBgYAoKLS0tCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAuNX0KZ2dwbG90KGRhdGEgPSBmbGlnaHRzX3N1YnNldCwgCiAgICBtYXBwaW5nID0gYWVzKHggPSB0ZW1wLCB5ID0gZGVwX2RlbGF5LCBjb2xvciA9IGNhcnJpZXIpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiBzdW1tZXIpCmBgYAoKLS0tCgojIyAxLiBgZ2dwbG90MmAgUHJpbWVzIFJlZ3Jlc3Npb24KCldoeT8gRGlnIGRlZXBlciBpbnRvIGRhdGEuIExvb2sgYXQgYG9yaWdpbmAgYW5kIGBkZXN0YCB2YXJpYWJsZXMgYXMgd2VsbDoKCjxicj4gCgpgYGB7cn0KZmxpZ2h0c19zdWJzZXQgJT4lIAogIGdyb3VwX2J5KGNhcnJpZXIsIG9yaWdpbiwgZGVzdCkgJT4lIAogIHN1bW1hcmlzZShgTnVtYmVyIG9mIEZsaWdodHNgID0gbigpKQpgYGAKCi0tLQoKCiMjIDIuIGBicm9vbWAgUGFja2FnZQoKKiBUaGUgYGJyb29tYCBwYWNrYWdlIHRha2VzIHRoZSBtZXNzeSBvdXRwdXQgb2YgYnVpbHQtaW4gbW9kZWxpbmcgZnVuY3Rpb25zIGluIFIsIHN1Y2ggYXMKYGxtYCwgYG5sc2AsIG9yIGB0LnRlc3RgLCBhbmQgdHVybnMgdGhlbSBpbnRvIHRpZHkgZGF0YSBmcmFtZXMuCiogRml0cyBpbiB3aXRoIGB0aWR5dmVyc2VgIGVjb3N5c3RlbQoqIFRoaXMgd29ya3MgZm9yIFttYW55IFIgZGF0YSB0eXBlc10oaHR0cHM6Ly9naXRodWIuY29tL3RpZHl2ZXJzZS9icm9vbSNhdmFpbGFibGUtdGlkaWVycykhCgotLS0KCiMjIDIuIGBicm9vbWAgUGFja2FnZQoKSW4gb3VyIGNhc2UsIGBicm9vbWAgZnVuY3Rpb25zIHRha2UgYGxtYCBvYmplY3RzIGFzIGlucHV0cyBhbmQgcmV0dXJuIHRoZSBmb2xsb3dpbmcgaW4gdGlkeSBmb3JtYXQhCgoqIGB0aWR5KClgOiByZWdyZXNzaW9uIG91dHB1dCB0YWJsZQoqIGBhdWdtZW50KClgOiBwb2ludC1ieS1wb2ludCB2YWx1ZXMgKGZpdHRlZCB2YWx1ZXMsIHJlc2lkdWFscywgcHJlZGljdGVkIHZhbHVlcykKKiBgZ2xhbmNlKClgOiBzY2FsYXIgc3VtbWFyaWVzIGxpa2UgPGxhcmdlPiAkUl4yJCA8L2xhcmdlPiwgCgotLS0KCiMjIDIuIGBicm9vbWAgUGFja2FnZQoKRXhhbXBsZQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKbGlicmFyeShrbml0cikKbGlicmFyeShicm9vbSkKc2V0LnNlZWQoMjAxNykKCiMgTG9hZCBBbGFza2EgZGF0YSwgZGVsZXRpbmcgcm93cyB0aGF0IGhhdmUgbWlzc2luZyBkZXBhcnR1cmUgZGVsYXkKIyBvciBhcnJpdmFsIGRlbGF5IGRhdGEKYWxhc2thX2ZsaWdodHMgPC0gZmxpZ2h0cyAlPiUgCiAgZmlsdGVyKGNhcnJpZXIgPT0gIkFTIikgJT4lIAogIGZpbHRlcighaXMubmEoZGVwX2RlbGF5KSAmICFpcy5uYShhcnJfZGVsYXkpKSAlPiUgCiAgc2FtcGxlX24oNTApCmBgYAoKLS0tCgpgYGB7cn0KIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQbG90IG9mIHNhbXBsZSBvZiBwb2ludHM6CmdncGxvdChkYXRhID0gYWxhc2thX2ZsaWdodHMsIG1hcHBpbmcgPSBhZXMoeCA9IGRlcF9kZWxheSwgeSA9IGFycl9kZWxheSkpICsgCiAgIGdlb21fcG9pbnQoKQpgYGAKCi0tLQoKYGBge3J9CiMgQ29ycmVsYXRpb24gY29lZmZpY2llbnQ6CmFsYXNrYV9mbGlnaHRzICU+JSBzdW1tYXJpemUoY29ycmVsID0gY29yKGRlcF9kZWxheSwgYXJyX2RlbGF5KSkKCiMgQWRkIHJlZ3Jlc3Npb24gbGluZQpnZ3Bsb3QoZGF0YSA9IGFsYXNrYV9mbGlnaHRzLCBtYXBwaW5nID0gYWVzKHggPSBkZXBfZGVsYXksIHkgPSBhcnJfZGVsYXkpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikKYGBgCgotLS0KCmBgYHtyfQojIEZpdCBSZWdyZXNzaW9uIGFuZCBTdHVkeSBPdXRwdXQgd2l0aCBicm9vbSBQYWNrYWdlLS0tLS0tLS0tLS0tLS0tLS0tLQojIEZpdCByZWdyZXNzaW9uCm9wdGlvbnMoc2NpcGVuID0gNikgIyBTZXQgc2NpZW50aWZpYyBub3RhdGlvbgpkZWxheV9maXQgPC0gbG0oZm9ybXVsYSA9IGFycl9kZWxheSB+IGRlcF9kZWxheSwgZGF0YSA9IGFsYXNrYV9mbGlnaHRzKQoKIyAxLiBicm9vbTo6dGlkeSgpIHJlZ3Jlc3Npb24gdGFibGUgd2l0aCBjb25maWRlbmNlIGludGVydmFscyAKIyBhbmQgbm8gcC12YWx1ZSBzdGFycwpyZWdyZXNzaW9uX3RhYmxlIDwtIGRlbGF5X2ZpdCAlPiUgCiAgdGlkeShjb25mLmludCA9IFRSVUUpCnJlZ3Jlc3Npb25fdGFibGUKYGBgCgotLS0KCmBgYHtyfQojIDIuIGJyb29tOjphdWdtZW50KCkgZm9yIHBvaW50LWJ5LXBvaW50IHZhbHVlcwpyZWdyZXNzaW9uX3BvaW50cyA8LSBkZWxheV9maXQgJT4lIAogIGF1Z21lbnQoKSAlPiUgCiAgc2VsZWN0KGFycl9kZWxheSwgZGVwX2RlbGF5LCAuZml0dGVkLCAucmVzaWQpIApyZWdyZXNzaW9uX3BvaW50cwpgYGAKCi0tLQoKYGBge3J9CiMgYW5kIGZvciBwcmVkaWN0aW9uCm5ld19mbGlnaHRzIDwtIGRhdGFfZnJhbWUoZGVwX2RlbGF5ID0gYygyNSwgMzAsIDE1KSkKZGVsYXlfZml0ICU+JSAKICBhdWdtZW50KG5ld2RhdGEgPSBuZXdfZmxpZ2h0cykKYGBgCgotLS0KCmBgYHtyfQojIDMuIGJyb29tOjpnbGFuY2UoKSBzY2FsYXIgc3VtbWFyaWVzIG9mIHJlZ3Jlc3Npb24KcmVncmVzc2lvbl9zdW1tYXJpZXMgPC0gZGVsYXlfZml0ICU+JSAKICBnbGFuY2UoKSAKcmVncmVzc2lvbl9zdW1tYXJpZXMKYGBgCgotLS0KCmBgYHtyfQojIFJlc2lkdWFsIEFuYWx5c2lzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmdncGxvdChkYXRhID0gcmVncmVzc2lvbl9wb2ludHMsIG1hcHBpbmcgPSBhZXMoeCA9IC5yZXNpZCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xMCwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiYmx1ZSIpCmBgYAoKLS0tCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSByZWdyZXNzaW9uX3BvaW50cywgbWFwcGluZyA9IGFlcyh4ID0gLmZpdHRlZCwgeSA9IC5yZXNpZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMCwgY29sb3IgPSAiYmx1ZSIpCmBgYAoKLS0tCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSByZWdyZXNzaW9uX3BvaW50cywgbWFwcGluZyA9IGFlcyhzYW1wbGUgPSAucmVzaWQpKSArCiAgc3RhdF9xcSgpCmBgYAoKLS0tCgpjbGFzczogY2VudGVyLCBtaWRkbGUsIGludmVyc2UKbmFtZTogcm1hcmtkb3duCgojIERhdGEgQ29tbXVuaWNhdGluZwoKCi0tLQoKIyMgV2hhdCBpcyBNYXJrZG93bj8KCiAtIEEgInBsYWludGV4dCBmb3JtYXR0aW5nIHN5bnRheCIKIC0gVHlwZSBpbiBwbGFpbiB0ZXh0LCByZW5kZXIgdG8gbW9yZSBjb21wbGV4IGZvcm1hdHMKIC0gT25lIHN0ZXAgYmV5b25kIHdyaXRpbmcgYSBgdHh0YCBmaWxlCiAtIFJlbmRlciB0byBIVE1MLCBQREYsIERPQ1gsIGV0Yy4gdXNpbmcgUGFuZG9jCgotLS0KCiMjIFdoYXQgZG9lcyBpdCBsb29rIGxpa2U/CgoubGVmdC1jb2x1bW5bCmBgYAogICMgSGVhZGVyIDEKICAKICAjIyBIZWFkZXIgMgogIAogIE5vcm1hbCBwYXJhZ3JhcGhzIG9mIHRleHQgZ28gaGVyZS4KICAKICAqKkknbSBib2xkKioKICAKICBbbGlua3MhXShodHRwOi8vcnN0dWRpby5jb20pCiAgCiAgICogVW5vcmRlcmVkCiAgICogTGlzdHMgICAKICAgCiAgQW5kICBUYWJsZXMKICAtLS0tIC0tLS0tLS0KICBMaWtlIFRoaXMKICAKYGBgCl0KCi5yaWdodC1jb2x1bW5bCjxpbWcgc3JjPSJmaWd1cmUvbWFya2Rvd24ucG5nIiBhbHQ9Im1hcmtkb3duIiBzdHlsZT0id2lkdGg6IDI3MHB4OyIvPgpdCgotLS0KCiMjIFdoYXQgaXMgUiBNYXJrZG93bj8KICAKLSAiTGl0ZXJhdGUgcHJvZ3JhbW1pbmciCi0gRW1iZWQgUiBjb2RlIGluIGEgTWFya2Rvd24gZG9jdW1lbnQKLSBSZW5kZXJzIHRleHR1YWwgb3V0cHV0IGFsb25nIHdpdGggZ3JhcGhpY3MKCioqKgoKLmxlZnQtY29sdW1uWwpgYGAKCmALYAtge3IgY2h1bmsxfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobnljZmxpZ2h0czEzKQpwZHhfZmxpZ2h0cyA8LSBmbGlnaHRzICU+JSAKICBmaWx0ZXIoZGVzdCA9PSAiUERYIiwgbW9udGggPT0gNSkKbnJvdyhwZHhfZmxpZ2h0cykKYAtgC2AKCmALYAtge3IgY2h1bmsyfQpnZ3Bsb3QoZGF0YSA9IHBkeF9mbGlnaHRzLAogIG1hcHBpbmcgPSBhZXMoeCA9IGFycl9kZWxheSwgCiAgICAgICAgICAgICAgICB5ID0gZGVwX2RlbGF5KSkgKwogIGdlb21fcG9pbnQoKQpgC2ALYAoKYGBgCl0KCi5yaWdodC1jb2x1bW5bCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGVjaG89RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShueWNmbGlnaHRzMTMpCnBkeF9mbGlnaHRzIDwtIGZsaWdodHMgJT4lIAogIGZpbHRlcihkZXN0ID09ICJQRFgiLCBtb250aCA9PSA1KQpucm93KHBkeF9mbGlnaHRzKQpnZ3Bsb3QoZGF0YSA9IHBkeF9mbGlnaHRzLAogIG1hcHBpbmcgPSBhZXMoeCA9IGFycl9kZWxheSwgCiAgICAgICAgICAgICAgICB5ID0gZGVwX2RlbGF5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKXQoKLS0tCgojIyBQcmFjdGljZQoKVHVybiBhIHN0YXRpc3RpY2FsIGFuYWx5c2lzIHlvdSBoYXZlIGNvbmR1Y3RlZCBpbnRvIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQuCgotIFlvdSBjYW4gYWxzbyBwdWJsaXNoIG9ubGluZSBlYXNpbHkgdG8gPGh0dHA6Ly9ycHVicy5jb20+CgotLS0KCmNsYXNzOiBtaWRkbGUKCiMgVGhhbmtzIGZvciBhdHRlbmRpbmchCgotIFNsaWRlcyBjcmVhdGVkIHZpYSB0aGUgUiBwYWNrYWdlIFt4YXJpbmdhbl0oaHR0cHM6Ly9naXRodWIuY29tL3lpaHVpL3hhcmluZ2FuKSBieSBZaWh1aSBYaWUKLSBTb3VyY2UgY29kZSBmb3IgdGhlc2Ugc2xpZGVzIGFuZCB0aGUgd2VicGFnZSBhdCA8aHR0cHM6Ly9naXRodWIuY29tL2lzbWF5Yy9wb1J0bGFuZC1ib290Y2FtcDE3Pg==